title: WPF: How to change value and keep binding
body: <p>I was looking for a way to highlight certain parts of a <code>TextBlock</code>'s content.</p>
  
  <p>Since most of the solutions I was finding suggested to write my own control that inherits from <code>TextBlock</code> and since I don't like to write my own control for every little bit of extra functionality I tried to put that funktionality in an attached behavior, and I was successful (so I thought).</p>
  
  <p>Here's my code for the behavior:</p>
  
  <pre><code>Public NotInheritable Class TextBlockHighlighting
  
      Private Sub New()
      End Sub
  
  #Region " HighlightTextProperty "
  
      Public Shared HighlightTextProperty As DependencyProperty = DependencyProperty.RegisterAttached("HighlightText", GetType(String), GetType(TextBlockHighlighting), New FrameworkPropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf TextBlockHighlighting.OnHighlightTextPropertyChanged)))
  
      &lt;AttachedPropertyBrowsableForType(GetType(TextBlock))&gt;
      Public Shared Function GetHighlightText(ByVal obj As TextBlock) As String
          Return obj.GetValue(TextBlockHighlighting.HighlightTextProperty)
      End Function
  
      &lt;AttachedPropertyBrowsableForType(GetType(TextBlock))&gt;
      Public Shared Sub SetHighlightText(ByVal obj As TextBlock, ByVal value As String)
          obj.SetValue(TextBlockHighlighting.HighlightTextProperty, value)
      End Sub
  
      Private Shared Sub OnHighlightTextPropertyChanged(ByVal sender As Object, ByVal e As DependencyPropertyChangedEventArgs)
          Dim tb As TextBlock
  
          tb = TryCast(sender, TextBlock)
  
          If (tb Is Nothing) Then
              Throw New InvalidOperationException("Error")
          End If
  
          Call TextBlockHighlighting.Refresh(tb)
      End Sub
  
  #End Region
  
  #Region " HighlightStyleProperty "
  
      Public Shared HighlightStyleProperty As DependencyProperty = DependencyProperty.RegisterAttached("HighlightStyle", GetType(Style), GetType(TextBlockHighlighting), New FrameworkPropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf TextBlockHighlighting.OnHighlightStylePropertyChanged)))
  
      &lt;AttachedPropertyBrowsableForType(GetType(TextBlock))&gt;
      Public Shared Function GetHighlightStyle(ByVal obj As TextBlock) As Style
          Return obj.GetValue(TextBlockHighlighting.HighlightStyleProperty)
      End Function
  
      &lt;AttachedPropertyBrowsableForType(GetType(TextBlock))&gt;
      Public Shared Sub SetHighlightStyle(ByVal obj As TextBlock, ByVal value As Style)
          obj.SetValue(TextBlockHighlighting.HighlightStyleProperty, value)
      End Sub
  
      Private Shared Sub OnHighlightStylePropertyChanged(ByVal sender As Object, ByVal e As DependencyPropertyChangedEventArgs)
          Dim tb As TextBlock
  
          tb = TryCast(sender, TextBlock)
  
          If (tb Is Nothing) Then
              Throw New InvalidOperationException("Error")
          End If
  
          Call TextBlockHighlighting.Refresh(tb)
      End Sub
  
  #End Region
  
      Private Shared Sub Refresh(ByVal sender As TextBlock)
          Dim highlight As String
          Dim style As Style
          Dim oldValue As String
  
          oldValue = sender.Text
  
          If String.IsNullOrEmpty(sender.Text) Then
              Exit Sub
          End If
  
          sender.Inlines.Clear()
          highlight = TextBlockHighlighting.GetHighlightText(sender)
          style = TextBlockHighlighting.GetHighlightStyle(sender)
  
          If (style Is Nothing) Then
              style = New Style(GetType(Run))
              style.Setters.Add(New Setter(Run.BackgroundProperty, Brushes.Green))
              style.Setters.Add(New Setter(Run.ForegroundProperty, Brushes.White))
          End If
  
          If String.IsNullOrEmpty(highlight) OrElse (oldValue.IndexOf(highlight, StringComparison.InvariantCultureIgnoreCase) &lt; 0) Then
              sender.Text = oldValue
          Else
              Dim index As Integer = oldValue.IndexOf(highlight, StringComparison.InvariantCultureIgnoreCase)
              Dim pos As Integer = 0
  
              Do While (index &gt;= 0)
                  Dim t As String
  
                  t = oldValue.Substring(pos, index - pos)
                  sender.Inlines.Add(t)
                  pos = index
  
                  t = oldValue.Substring(pos, highlight.Length)
                  sender.Inlines.Add(New Run(t) With {.Style = style})
                  pos += highlight.Length
                  index = oldValue.IndexOf(highlight, pos, StringComparison.InvariantCultureIgnoreCase)
              Loop
  
              sender.Inlines.Add(New Run(oldValue.Substring(pos)))
          End If
      End Sub
  
  End Class
  </code></pre>
  
  <p>Here's my code for the view model (I leave the code of the RelayCommand-class for your to fill in, I guess everyone is having an implementation of it). You could even get rid of it and call the function <code>ChangeText</code> in code behind instead:</p>
  
  <pre><code>Imports System.ComponentModel
  
  Public Class MainViewModel
      Implements INotifyPropertyChanged
  
      Private _highlightText As String
      Private _text As String
      Private _changeTextCommand As ICommand = New RelayCommand(AddressOf Me.ChangeText)
  
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
  
      Public Sub New()
          Me.HighlightText = "lo"
          _text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
      End Sub
  
      Private Sub OnPropertyChanged(propertyName As String)
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
      End Sub
  
      Public Property HighlightText As String
          Get
              Return _highlightText
          End Get
          Set(value As String)
              _highlightText = value
              Me.OnPropertyChanged("HighlightText")
          End Set
      End Property
  
      Public ReadOnly Property Text As String
          Get
              Return _text
          End Get
      End Property
  
      Public ReadOnly Property ChangeTextCommand As ICommand
          Get
              Return _changeTextCommand
          End Get
      End Property
  
      Private Sub ChangeText()
          _text = "This is another text containing the default highlight text ""lo""."
          Me.OnPropertyChanged("Text")
      End Sub
  
  End Class
  </code></pre>
  
  <p>And finally here's my Xaml for the main window:</p>
  
  <pre><code>&lt;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:WpfApp2"
          mc:Ignorable="d"
          Title="MainWindow" Height="350" Width="525"&gt;
      &lt;Window.DataContext&gt;
          &lt;local:MainViewModel /&gt;
      &lt;/Window.DataContext&gt;
  
      &lt;Window.Resources&gt;
          &lt;Style x:Key="HightlightStyle" TargetType="Run"&gt;
              &lt;Setter Property="Background" Value="LightGreen" /&gt;
              &lt;Setter Property="Foreground" Value="Yellow" /&gt;
          &lt;/Style&gt;
      &lt;/Window.Resources&gt;
  
      &lt;Grid&gt;
          &lt;Grid.RowDefinitions&gt;
              &lt;RowDefinition Height="Auto" /&gt;
              &lt;RowDefinition Height="Auto" /&gt;
              &lt;RowDefinition Height="Auto" /&gt;
          &lt;/Grid.RowDefinitions&gt;
          &lt;Grid.ColumnDefinitions&gt;
              &lt;ColumnDefinition Width="Auto" /&gt;
              &lt;ColumnDefinition Width="*" /&gt;
          &lt;/Grid.ColumnDefinitions&gt;
  
          &lt;Label Grid.Row="0" Grid.Column="0" Content="Hightlight text:" /&gt;
          &lt;TextBox Grid.Row="0" Grid.Column="1" Text="{Binding HighlightText, UpdateSourceTrigger=PropertyChanged}" /&gt;
  
          &lt;TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Text}" TextWrapping="Wrap"
                     local:TextBlockHighlighting.HighlightText="{Binding HighlightText}"
                     local:TextBlockHighlighting.HighlightStyle="{StaticResource HightlightStyle}" /&gt;
  
          &lt;Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="Change text" Margin="100,5" Command="{Binding ChangeTextCommand}" /&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>
  
  <p>And here's my problem. When I start this program and hit the button "Change text" without doing anything else before, the text gets changed (like expected).</p>
  
  <p>But when I start the program, change the hightlight text (and therefore the highlighting) and hit the button "Change text" after that, nothing happens.</p>
  
  <p>After a lot of searching and debugging and trying I think the reason for that is, that in my attached behavior I change the <code>Inlines</code> collection of the <code>TextBlock</code> and that breaks the binding to the <code>Text</code> property.</p>
  
  <p>So, how can I change the <code>Inlines</code> collection of the <code>TextBlock</code> (which itself is not bindable) without breaking the binding to the <code>Text</code> property?
  Or how can I achieve my goal on other ways?</p>
  
  <p>Thank you for your help.</p>