wpfdata-bindingbindingtextchangedaddhandler

WPF Custom Textbox control within UserControl RelativeSource causing TextChanged event to fire even after loaded


I have a custom user control defined in XAML like this:

<UserControl x:Class="MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyApplicationName"
             xmlns:wpf="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
             mc:Ignorable="d"
             >

Within it, I have a custom textbox control, called CustomIndicator which inherits TextBox in code-behind, placed in XAML like this:

<local:CustomIndicator x:Name = "Parameter1" Background="{Binding ElementName=Parameter1, Path=OffColor}" Text="{Binding myUserParameter1, StringFormat=N1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}" OnText="1" OffText="1" ModuleKey="D1" ModuleName="1" Height="20"/>

After the user control is loaded, in Me.Loaded, I loop through all of the controls and add a TextChanged handler to the CustomIndicator (recall it is a custom Textbox control) like this:

AddHandler TryCast(ctrl, TextBox).TextChanged, Sub(sender, e) ControlValueChanged(sender, e)

I'm positive it's being added after both A) the user control is initialized/loaded and B) the custom textbox control, CustomIndicator, is initialized/loaded...I put a breakpoint at the adding of the handler and confirmed both are IsInitialized/IsLoaded=True. However, the TextChanged event is still firing because it appears that the binding is still occurring yet later. If I change the XAML to this, it doesn't fire, but I also don't get the value:

<local:CustomIndicator x:Name = "Parameter1" Background="{Binding ElementName=Parameter1, Path=OffColor}" Text="{Binding myUserParameter1, StringFormat=N1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" OnText="1" OffText="1" ModuleKey="D1" ModuleName="1" Height="20"/>

The removal of the "RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" makes the TextChanged stop firing late, but I also don't get the value. How can I add the handler such that it is, truly, after everything is loaded, including the binding?

Running a trace, by the way, did reveal the following:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=myUserParameter1; DataItem=null; target element is 'CustomIndicator' (Name='Parameter1'); target property is 'Text' (type 'String')

Don't know if the trace helps...but I'll also mention that myUserParameter1 holds the value of 0 (or some other numeric value, which is set in the Loaded="Window_Loaded" event of the main window), and the textbox is still blank by the end of the Me.Loaded event.


Solution

  • Though it isn't really solved, I found a solution that works. The two things I did were 1) added a FallbackVaue to my bindings, and 2) changed the order of the user control instance creation and update of values. While those two things don't fully explain what was going on, they're good enough to provide the needed functionality.