Here is my searchtextbox class, the actual textbox inside the XAML design is PART_TextBox, I have tried using PART_TextBox_TextChanged, TextBox_TextChanged, and OnTextBox_TextChanged, none work, and .text is empty always.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SearchTextBox
{
[TemplatePart(Name = PartRootPanelName, Type = typeof(Panel))]
[TemplatePart(Name = PartTextBoxName, Type = typeof(TextBox))]
[TemplatePart(Name = PartSearchIconName, Type = typeof(Button))]
[TemplatePart(Name = PartCloseButtonName, Type = typeof(Button))]
public class SearchTextBox : Control
{
    private const string PartRootPanelName = "PART_RootPanel";
    private const string PartTextBoxName = "PART_TextBox";
    private const string PartSearchIconName = "PART_SearchIcon";
    private const string PartCloseButtonName = "PART_CloseButton";
    // Commands.
    public static readonly RoutedCommand ActivateSearchCommand;
    public static readonly RoutedCommand CancelSearchCommand;
    // Properties.
    public static readonly DependencyProperty HandleClickOutsidesProperty;
    public static readonly DependencyProperty UpdateDelayMillisProperty;
    public static readonly DependencyProperty HintTextProperty;
    public static readonly DependencyProperty DefaultFocusedElementProperty;
    public static readonly DependencyProperty TextBoxTextProperty;
    public static readonly DependencyProperty TextBoxTextChangedProperty;
    static SearchTextBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchTextBox), new FrameworkPropertyMetadata(typeof(SearchTextBox)));
        ActivateSearchCommand = new RoutedCommand();
        CancelSearchCommand = new RoutedCommand();
        // // Using of CommandManager.
        // // https://www.codeproject.com/Articles/43295/ZoomBoxPanel-add-custom-commands-to-a-WPF-control
        CommandManager.RegisterClassCommandBinding(typeof(SearchTextBox),
            new CommandBinding(ActivateSearchCommand, ActivateSearchCommand_Invoke));
        CommandManager.RegisterClassCommandBinding(typeof(SearchTextBox),
            new CommandBinding(CancelSearchCommand, CancelSearchCommand_Invoke));
        // Register properties.
        HandleClickOutsidesProperty = DependencyProperty.Register(
            nameof(HandleClickOutsides), typeof(bool), typeof(SearchTextBox),
            new UIPropertyMetadata(true));
        // // Set default value.
        // // https://stackoverflow.com/questions/6729568/how-can-i-set-a-default-value-for-a-dependency-property-of-type-derived-from-dep
        UpdateDelayMillisProperty = DependencyProperty.Register(
            nameof(UpdateDelayMillis), typeof(int), typeof(SearchTextBox),
            new UIPropertyMetadata(1000));
        HintTextProperty = DependencyProperty.Register(
        nameof(HintText), typeof(string), typeof(SearchTextBox),
        new UIPropertyMetadata("Set HintText property"));
        DefaultFocusedElementProperty = DependencyProperty.Register(
            nameof(DefaultFocusedElement), typeof(UIElement), typeof(SearchTextBox));
        TextBoxTextProperty =
            DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(SearchTextBox));
       
    }
    public string Text 
    {
        get { return (string)GetValue(TextBoxTextProperty);  }
        set { SetValue(TextBoxTextProperty, value);  }
    }
    public static void ActivateSearchCommand_Invoke(object sender, ExecutedRoutedEventArgs e)
    {
        if (sender is SearchTextBox self)
            self.ActivateSearch();
    }
    public static void CancelSearchCommand_Invoke(object sender, ExecutedRoutedEventArgs e)
    {
        if (sender is SearchTextBox self)
        {
            self.textBox.Text = "";
            self.CancelPreviousSearchFilterUpdateTask();
            self.UpdateFilterText();
            self.DeactivateSearch();
        }
    }
    private static UIElement GetFirstSelectedControl(Selector list)
        => list.SelectedItem == null ? null :
            list.ItemContainerGenerator.ContainerFromItem(list.SelectedItem) as UIElement;
    private static UIElement GetDefaultSelectedControl(Selector list)
        => list.ItemContainerGenerator.ContainerFromIndex(0) as UIElement;
    // Events.
    // // Using of events.
    // // https://stackoverflow.com/questions/13447940/how-to-create-user-define-new-event-for-user-control-in-wpf-one-small-example
    public event EventHandler SearchTextFocused;
    public event EventHandler SearchTextUnfocused;
    // // Parameter passing:
    // // https://stackoverflow.com/questions/4254636/how-to-create-a-custom-event-handling-class-like-eventargs
    public event EventHandler<string> SearchRequested;
    public event TextChangedEventHandler TextChanged;
    
    // Parts.
    private Panel rootPanel;
    private TextBox textBox;
    private Button searchIcon;
    private Label closeButton;
    // Handlers.
    // Field for click-outsides handling.
    private readonly MouseButtonEventHandler windowWideMouseButtonEventHandler;
    // Other fields.
    private CancellationTokenSource waitingSearchUpdateTaskCancellationTokenSource;
    // <init>
    public SearchTextBox()
    {
        // Click events in the window will be previewed by
        // function OnWindowWideMouseEvent (defined later)
        // when the handler is on. Now it's off.
        windowWideMouseButtonEventHandler =
            new MouseButtonEventHandler(OnWindowWideMouseEvent);
       
    }
   
    // Properties.
    
    public bool HandleClickOutsides
    {
        get => (bool)GetValue(HandleClickOutsidesProperty);
        set => SetValue(HandleClickOutsidesProperty, value);
    }
    public int UpdateDelayMillis
    {
        get => (int)GetValue(UpdateDelayMillisProperty);
        set => SetValue(UpdateDelayMillisProperty, value);
    }
    public string HintText
    {
        get => (string)GetValue(HintTextProperty);
        set => SetValue(HintTextProperty, value);
    }
    public UIElement DefaultFocusedElement
    {
        get => (UIElement)GetValue(DefaultFocusedElementProperty);
        set => SetValue(DefaultFocusedElementProperty, value);
    }
    //Event handler functions.
    // This would only be on whenever search box is focused.
    private void OnWindowWideMouseEvent(object sender, MouseButtonEventArgs e)
    {
        // By clicking outsides the search box deactivate the search box.
        if (!IsMouseOver) DeactivateSearch();
    }
    private void PART_TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextChanged?.Invoke(this, e);
    }
    public void OnTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        SearchTextFocused?.Invoke(this, e);
        if (HandleClickOutsides)
            // Get window.
            // https://stackoverflow.com/questions/302839/wpf-user-control-parent
            Window.GetWindow(this).AddHandler(
                Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
    }
    public void OnTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        SearchTextUnfocused?.Invoke(this, e);
        if (HandleClickOutsides)
            Window.GetWindow(this).RemoveHandler(
                Window.PreviewMouseDownEvent, windowWideMouseButtonEventHandler);
    }
    private void OnTextBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Escape)
            CancelSearchCommand.Execute(null, this);
        else if (e.Key == Key.Enter)
        {
            CancelPreviousSearchFilterUpdateTask();
            UpdateFilterText();
        }
        else
        {
            CancelPreviousSearchFilterUpdateTask();
            // delay == 0: Update now;
            // delay < 0: Don't update except Enter or Esc;
            // delay > 0: Delay and update.
            var delay = UpdateDelayMillis;
            if (delay == 0) UpdateFilterText();
            else if (delay > 0)
            {
                // // Delayed task.
                // // https://stackoverflow.com/questions/15599884/how-to-put-delay-before-doing-an-operation-in-wpf
                waitingSearchUpdateTaskCancellationTokenSource = new CancellationTokenSource();
                Task.Delay(delay, waitingSearchUpdateTaskCancellationTokenSource.Token)
                   .ContinueWith(self => {
                       if (!self.IsCanceled) Dispatcher.Invoke(() => UpdateFilterText());
                   });
            }
        }
    }
    // Public interface.
    public void ActivateSearch()
    {
        textBox?.Focus();
    }
    public void DeactivateSearch()
    {
        // // Use keyboard focus instead.
        // // https://stackoverflow.com/questions/2914495/wpf-how-to-programmatically-remove-focus-from-a-textbox
        //Keyboard.ClearFocus();
        if (DefaultFocusedElement != null)
        {
            UIElement focusee = null;
            if (DefaultFocusedElement is Selector list)
            {
                focusee = GetFirstSelectedControl(list);
                if (focusee == null)
                    focusee = GetDefaultSelectedControl(list);
            }
            if (focusee == null) focusee = DefaultFocusedElement;
            Keyboard.Focus(focusee);
        }
        else
        {
            rootPanel.Focusable = true;
            Keyboard.Focus(rootPanel);
            rootPanel.Focusable = false;
        }
    }
    // Helper functions.
    private void CancelPreviousSearchFilterUpdateTask()
    {
        if (waitingSearchUpdateTaskCancellationTokenSource != null)
        {
            waitingSearchUpdateTaskCancellationTokenSource.Cancel();
            waitingSearchUpdateTaskCancellationTokenSource.Dispose();
            waitingSearchUpdateTaskCancellationTokenSource = null;
        }
    }
    private void UpdateFilterText() => SearchRequested?.Invoke(this, textBox.Text);
    // .
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        // // Idea of detaching.
        // // https://www.jeff.wilcox.name/2010/04/template-part-tips/
        if (textBox != null)
        {
            textBox.GotKeyboardFocus -= OnTextBox_GotFocus;
            textBox.LostKeyboardFocus -= OnTextBox_LostFocus;
            textBox.KeyUp -= OnTextBox_KeyUp;
        }
        rootPanel = GetTemplateChild(PartRootPanelName) as Panel;
        textBox = GetTemplateChild(PartTextBoxName) as TextBox;
        searchIcon = GetTemplateChild(PartSearchIconName) as Button;
        closeButton = GetTemplateChild(PartCloseButtonName) as Label;
        if (textBox != null)
        {
            textBox.GotKeyboardFocus += OnTextBox_GotFocus;
            textBox.LostKeyboardFocus += OnTextBox_LostFocus;
            textBox.KeyUp += OnTextBox_KeyUp;
            }
        }
    }
}
My XAML:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SearchTextBox" xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase">
<Style TargetType="{x:Type local:SearchTextBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:SearchTextBox}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid Name="PART_RootPanel">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="28"/>
                        </Grid.ColumnDefinitions>
                        <Grid Grid.Column="0">
                            <TextBox Name="PART_TextBox" Background="#FF333337" BorderThickness="0" VerticalContentAlignment="Center" Foreground="White" FontSize="14px" Text=""/>
                            <TextBlock IsHitTestVisible="False" Text="{TemplateBinding HintText}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="4,0,0,0" FontSize="14px" Foreground="#FF7C7777">
                                <TextBlock.Style>
                                    <Style  TargetType="{x:Type TextBlock}">
                                        <Setter Property="Visibility" Value="Collapsed"/>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Text, ElementName=PART_TextBox}" Value="">
                                                <Setter Property="Visibility" Value="Visible"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </Grid>
                        <Button Width="28" Grid.Column="1" Name="PART_SearchIcon"  Content="" Background="#FF252526"
                                Focusable="False" Command="{x:Static local:SearchTextBox.ActivateSearchCommand}">
                            <Button.Template>
                                <ControlTemplate TargetType="Button">
                                    <Grid Background="#FF333337">
                                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                    </Grid>
                                </ControlTemplate>
                            </Button.Template>
                            <Button.Style>
                                <Style TargetType="Button">
                                    <Setter Property="Visibility" Value="Collapsed"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Text, ElementName=PART_TextBox}" Value="">
                                            <Setter Property="Visibility" Value="Visible"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Button.Style>
                        </Button>
                        <Label Grid.Column="1" Width="28" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Cursor="Hand" VerticalContentAlignment="Center" VerticalAlignment="Stretch" Margin="0" Padding="4" Foreground="White" FontWeight="Bold" Content="x" Name="PART_CloseButton" Focusable="False"
                                   Background="#FF333337">
                            <Label.InputBindings>
                                <MouseBinding Command="{x:Static local:SearchTextBox.CancelSearchCommand}" MouseAction="LeftClick" />
                            </Label.InputBindings>
                         
                            <Label.Style>
                                <Style TargetType="Label">
                                    <Setter Property="Visibility" Value="Visible"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Text, ElementName=PART_TextBox}" Value="">
                                            <Setter Property="Visibility" Value="Collapsed"/>
                                        </DataTrigger>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Background" Value="#000"/>
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </Label.Style>
                        </Label>
                       
                    </Grid>    
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
The control builds fine, but I cannot get searchTextBox.Text, it returns null, and on TextChanged does not fire. Any ideas?
 
    