SelectionChanged 在 wpf 自定义控件中的 OnApplyTemplate 之前调用

SelectionChanged Called before OnApplyTemplate in wpf Custom Control

我正在开发一个名为 select 2 的自定义控件,灵感来自 WPF Auto Complete。问题是我的 OnSelectionChanged (与名为 SelectedItem 的 Dependecy 属性 绑定) 在 OnApplyTemplate (所有控件都为自定义控件初始化)。因为在更新模式下,项目没有像 OnApplyTemplate 那样 selected

using Helpers.CustomControls.AutoComplete;
using Helpers.CustomControls.AutoComplete.CustomPopupControl;
using Helpers.CustomControls.AutoComplete.Interfaces;
using System;
using System.Collections;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Helpers.CustomControls.AutoComplete
{
    [TemplatePart(Name = PartGridContainer, Type = typeof(Grid))]
    [TemplatePart(Name = PartEditor, Type = typeof(TextBox))]
    [TemplatePart(Name = PartSelect2, Type = typeof(TextBox))]
    [TemplatePart(Name = PartSuggestionBoxContainer, Type = typeof(StackPanel))]
    [TemplatePart(Name = PartPopup, Type = typeof(Popup))]
    [TemplatePart(Name = PartSelector, Type = typeof(Selector))]
    [TemplatePart(Name = Select2DownArrow, Type = typeof(Path))]
    [TemplatePart(Name = Select2UpArrow, Type = typeof(Path))]
    public class AutoCompleteTextBox : Control
    {

        #region "Fields"
        public const string PartGridContainer = "PART_GridContainer";
        public const string PartEditor = "PART_Editor";
        public const string PartPopup = "PART_Popup";
        public const string PartSelect2 = "PART_Select2";
        public const string PartSuggestionBoxContainer = "PART_SuggestionBoxContainer";
        public const string PartSelector = "PART_Selector";
        public const string Select2UpArrow = "Select2UpArrow";
        public const string Select2DownArrow = "Select2DownArrow";
        public static readonly DependencyProperty DelayProperty = DependencyProperty.Register("Delay", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(200));
        public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register("DisplayMember", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));
        public static readonly DependencyProperty IconPlacementProperty = DependencyProperty.Register("IconPlacement", typeof(IconPlacement), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(IconPlacement.Left));
        public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
        public static readonly DependencyProperty IconVisibilityProperty = DependencyProperty.Register("IconVisibility", typeof(Visibility), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(Visibility.Visible));
        public static readonly DependencyProperty IsSelect2Property = DependencyProperty.Register("IsSelect2", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register("IsLoading", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
        public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(AutoCompleteTextBox));
        public static readonly DependencyProperty LoadingContentProperty = DependencyProperty.Register("LoadingContent", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
        public static readonly DependencyProperty ProviderProperty = DependencyProperty.Register("Provider", typeof(ISuggestionProvider), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null));
        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(null, OnSelectedItemChanged));
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));
        public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(0));
        public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register("CharacterCasing", typeof(CharacterCasing), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(CharacterCasing.Normal));
        public static readonly DependencyProperty MaxPopUpHeightProperty = DependencyProperty.Register("MaxPopUpHeight", typeof(int), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(600));

        public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(string.Empty));

        public static readonly DependencyProperty SuggestionBackgroundProperty = DependencyProperty.Register("SuggestionBackground", typeof(Brush), typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(Brushes.White));
        private bool _isUpdatingText;
        private bool _selectionCancelled;

        private SuggestionsAdapter _suggestionsAdapter;


        #endregion

        #region "Constructors"

        static AutoCompleteTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(typeof(AutoCompleteTextBox)));

        }
        #endregion

        #region "Properties"


        public int MaxPopupHeight
        {
            get => (int)GetValue(MaxPopUpHeightProperty);
            set => SetValue(MaxPopUpHeightProperty, value);
        }


        public BindingEvaluator BindingEvaluator { get; set; }

        public CharacterCasing CharacterCasing
        {
            get => (CharacterCasing)GetValue(CharacterCasingProperty);
            set => SetValue(CharacterCasingProperty, value);
        }

        public int MaxLength
        {
            get => (int)GetValue(MaxLengthProperty);
            set => SetValue(MaxLengthProperty, value);
        }

        public int Delay
        {
            get => (int)GetValue(DelayProperty);

            set => SetValue(DelayProperty, value);
        }

        public string DisplayMember
        {
            get => (string)GetValue(DisplayMemberProperty);

            set => SetValue(DisplayMemberProperty, value);
        }

        private TextBox Select2 { get; set; }
        private TextBox Editor { get; set; }

        public DispatcherTimer FetchTimer { get; set; }

        public string Filter { get; set; }

        public object Icon
        {
            get => GetValue(IconProperty);

            set => SetValue(IconProperty, value);
        }

        public IconPlacement IconPlacement
        {
            get => (IconPlacement)GetValue(IconPlacementProperty);

            set => SetValue(IconPlacementProperty, value);
        }

        public Visibility IconVisibility
        {
            get => (Visibility)GetValue(IconVisibilityProperty);

            set => SetValue(IconVisibilityProperty, value);
        }
        public bool IsSelect2
        {
            get => (bool)GetValue(IsSelect2Property);

            set => SetValue(IsSelect2Property, value);
        }

        public bool IsDropDownOpen
        {
            get => (bool)GetValue(IsDropDownOpenProperty);

            set => SetValue(IsDropDownOpenProperty, value);
        }

        public bool IsLoading
        {
            get => (bool)GetValue(IsLoadingProperty);

            set => SetValue(IsLoadingProperty, value);
        }

        public bool IsReadOnly
        {
            get => (bool)GetValue(IsReadOnlyProperty);

            set => SetValue(IsReadOnlyProperty, value);
        }

        public Selector ItemsSelector { get; set; }

        public DataTemplate ItemTemplate
        {
            get => (DataTemplate)GetValue(ItemTemplateProperty);

            set => SetValue(ItemTemplateProperty, value);
        }

        public DataTemplateSelector ItemTemplateSelector
        {
            get => ((DataTemplateSelector)(GetValue(ItemTemplateSelectorProperty)));
            set => SetValue(ItemTemplateSelectorProperty, value);
        }

        public object LoadingContent
        {
            get => GetValue(LoadingContentProperty);

            set => SetValue(LoadingContentProperty, value);
        }
        private Grid GridContainer { get; set; }
        private Popup Select2Popup { get; set; }
        private StackPanel SuggestionBoxContainer { get; set; }
        private Popup Popup { get; set; }



        public ISuggestionProvider Provider
        {
            get => (ISuggestionProvider)GetValue(ProviderProperty);

            set => SetValue(ProviderProperty, value);
        }

        public object SelectedItem
        {
            get => GetValue(SelectedItemProperty);

            set => SetValue(SelectedItemProperty, value);
        }

        public SelectionAdapter SelectionAdapter { get; set; }

        public string Text
        {
            get => (string)GetValue(TextProperty);

            set => SetValue(TextProperty, value);
        }

        public string Watermark
        {
            get => (string)GetValue(WatermarkProperty);

            set => SetValue(WatermarkProperty, value);
        }
        public Brush SuggestionBackground
        {
            get => (Brush)GetValue(SuggestionBackgroundProperty);

            set => SetValue(SuggestionBackgroundProperty, value);
        }

        #endregion

        #region "Methods"

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            GridContainer = Template.FindName(PartGridContainer, this) as Grid;
            Select2 = Template.FindName(PartSelect2, this) as TextBox;
            Editor = Template.FindName(PartEditor, this) as TextBox;
            Popup = Template.FindName(PartPopup, this) as Popup;
            ItemsSelector = Template.FindName(PartSelector, this) as Selector;

            if (IsSelect2)
            {
                SuggestionBoxContainer = Template.FindName(PartSuggestionBoxContainer, this) as StackPanel;
                if (GridContainer != null)
                {
                    GridContainer.Children.Remove(SuggestionBoxContainer);
                    Select2Popup = InitializeSelect2Popup();
                    Select2Popup.Child = SuggestionBoxContainer;
                    GridContainer.Children.Add(Select2Popup);
                    Grid.SetRow(Select2Popup, 1);
                }
                if (Select2 != null)
                {
                    Select2.PreviewKeyDown += OnSelect2PreviewKeyDown;
                    Select2.PreviewMouseDown += OnSelect2PreviewMouseDown;

                }
            }
            else
            {
                if (Select2 != null)
                {
                    Select2.Visibility = Visibility.Collapsed;
                }
            }



            BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));

            if (Editor != null)
            {
                Editor.TextChanged += OnEditorTextChanged;
                Editor.PreviewKeyDown += OnEditorKeyDown;
                Editor.LostFocus += OnEditorLostFocus;

                if (SelectedItem != null)
                {
                    _isUpdatingText = true;
                    Editor.Text = BindingEvaluator.Evaluate(SelectedItem);
                    _isUpdatingText = false;
                }

            }

            if (Popup != null)
            {
                Popup.StaysOpen = false;
                Popup.Opened += OnPopupOpened;
                Popup.Closed += OnPopupClosed;
            }

            if (ItemsSelector != null)
            {
                SelectionAdapter = new SelectionAdapter(ItemsSelector);
                SelectionAdapter.Commit += OnSelectionAdapterCommit;
                SelectionAdapter.Cancel += OnSelectionAdapterCancel;
                SelectionAdapter.SelectionChanged += OnSelectionAdapterSelectionChanged;
                ItemsSelector.PreviewMouseDown += ItemsSelector_PreviewMouseDown;
            }
        }

        public static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
       {
            AutoCompleteTextBox act = null;
            act = d as AutoCompleteTextBox;
            if (act != null)
            {
                if (act.Editor != null & !act._isUpdatingText)
                {
                    act._isUpdatingText = true;
                    act.Editor.Text = act.BindingEvaluator.Evaluate(e.NewValue);
                    if (act.Select2 != null)
                    {
                        act.Select2.Text = act.BindingEvaluator.Evaluate(e.NewValue);
                    }
                    act._isUpdatingText = false;
                }
            }
        }

        private void ScrollToSelectedItem()
        {
            if (ItemsSelector is ListBox listBox && listBox.SelectedItem != null)
                listBox.ScrollIntoView(listBox.SelectedItem);
        }



        private void ItemsSelector_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if ((e.OriginalSource as FrameworkElement)?.DataContext == null)
                return;
            if (!ItemsSelector.Items.Contains(((FrameworkElement)e.OriginalSource)?.DataContext))
                return;
            ItemsSelector.SelectedItem = ((FrameworkElement)e.OriginalSource)?.DataContext;
            OnSelectionAdapterCommit();
        }

        private string GetDisplayText(object dataItem)
        {
            if (BindingEvaluator == null)
            {
                BindingEvaluator = new BindingEvaluator(new Binding(DisplayMember));
            }
            if (dataItem == null)
            {
                return string.Empty;
            }
            if (string.IsNullOrEmpty(DisplayMember))
            {
                return dataItem.ToString();
            }
            return BindingEvaluator.Evaluate(dataItem);
        }

        private void OnEditorKeyDown(object sender, KeyEventArgs e)
        {
            if (IsSelect2 && (e.Key == Key.Escape || (IsDropDownOpen == false && e.Key == Key.Enter)))
            {
                CloseSelect2Suggestions();
                e.Handled = true;
                return;
            }
            if (SelectionAdapter != null)
            {
                if (IsDropDownOpen)
                    SelectionAdapter.HandleKeyDown(e);
                else
                    IsDropDownOpen = e.Key == Key.Down || e.Key == Key.Up;
            }
        }

        private void OnEditorLostFocus(object sender, RoutedEventArgs e)
        {
            if (!IsKeyboardFocusWithin)
            {
                IsDropDownOpen = false;
            }
        }

        private void OnEditorTextChanged(object sender, TextChangedEventArgs e)
        {
            if (_isUpdatingText)
                return;
            if (FetchTimer == null)
            {
                FetchTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(Delay) };
                FetchTimer.Tick += OnFetchTimerTick;
            }
            FetchTimer.IsEnabled = false;
            FetchTimer.Stop();
            if (IsSelect2 == false)
            {
                SetSelectedItem(null);
            }
            if (Editor.Text.Length > 0)
            {
                FetchTimer.IsEnabled = true;
                FetchTimer.Start();
            }
            else
            {
                IsDropDownOpen = false;
            }
        }

        private void OnSelect2PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key != Key.Enter && e.Key != Key.Tab)
            {
                e.Handled = true;
                return;
            }
            if (e.Key == Key.Enter)
            {
                ToggleSelect2();
            }

        }
        private void OnSelect2PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            ToggleSelect2();
            e.Handled = true;
            return;
        }

        private void OnFetchTimerTick(object sender, EventArgs e)
        {
            FetchTimer.IsEnabled = false;
            FetchTimer.Stop();
            if (Provider != null && ItemsSelector != null)
            {
                Filter = Editor.Text;
                if (_suggestionsAdapter == null)
                {
                    _suggestionsAdapter = new SuggestionsAdapter(this);
                }
                _suggestionsAdapter.GetSuggestions(Filter);
            }
        }

        private void OnPopupClosed(object sender, EventArgs e)
        {
            if (!_selectionCancelled)
            {
                OnSelectionAdapterCommit();
            }
        }

        private void OnPopupOpened(object sender, EventArgs e)
        {
            _selectionCancelled = false;
            ItemsSelector.SelectedItem = SelectedItem;
        }

        private void OnSelectionAdapterCancel()
        {
            _isUpdatingText = true;
            Editor.Text = SelectedItem == null ? Filter : GetDisplayText(SelectedItem);
            Editor.SelectionStart = Editor.Text.Length;
            Editor.SelectionLength = 0;
            _isUpdatingText = false;
            IsDropDownOpen = false;
            _selectionCancelled = true;
        }

        private void OnSelectionAdapterCommit()
        {
            if (ItemsSelector.SelectedItem != null)
            {
                SelectedItem = ItemsSelector.SelectedItem;
                _isUpdatingText = true;
                Editor.Text = GetDisplayText(ItemsSelector.SelectedItem);
                if (IsSelect2 && Select2 != null)
                    Select2.Text = GetDisplayText(ItemsSelector.SelectedItem);
                SetSelectedItem(ItemsSelector.SelectedItem);
                _isUpdatingText = false;
                IsDropDownOpen = false;
                if (IsSelect2)
                    CloseSelect2Suggestions();
            }
        }

        private void OnSelectionAdapterSelectionChanged()
        {
            _isUpdatingText = true;
            Editor.Text = ItemsSelector.SelectedItem == null ? Filter : GetDisplayText(ItemsSelector.SelectedItem);
            Editor.SelectionStart = Editor.Text.Length;
            Editor.SelectionLength = 0;
            ScrollToSelectedItem();
            _isUpdatingText = false;
        }

        private void SetSelectedItem(object item)
        {
            _isUpdatingText = true;
            SelectedItem = item;
            _isUpdatingText = false;
        }
        #endregion

        #region[Supportive Functions]
        private void OpenSelect2Suggestions()
        {
            if (Select2Popup.IsOpen == false)
            {
                if (Select2Popup != null)
                    Select2Popup.IsOpen = true;
                Editor?.Focus();
                IsDropDownOpen = false;

                var upArrow = (Template.FindName(Select2UpArrow, this) as Path);
                var downArrow = (Template.FindName(Select2DownArrow, this) as Path);
                if (upArrow != null && downArrow != null)
                {
                    upArrow.Visibility = Visibility.Visible;
                    downArrow.Visibility = Visibility.Collapsed;
                }
            }
        }
        private void CloseSelect2Suggestions()
        {
            if (Select2Popup.IsOpen == true)
            {
                if (Select2Popup != null)
                    Select2Popup.IsOpen = false;
                Select2?.Focus();
                IsDropDownOpen = false;
            }
        }
        private void ToggleSelect2()
        {
            if (Select2Popup.IsOpen)
                CloseSelect2Suggestions();
            else
                OpenSelect2Suggestions();
        }
        private PopupNonTopmost InitializeSelect2Popup()
        {
            PopupNonTopmost select2Popup = new PopupNonTopmost();
            select2Popup.HorizontalAlignment = HorizontalAlignment.Stretch;
            select2Popup.Width = 300;
            select2Popup.IsOpen = false;
            select2Popup.PlacementTarget = Select2;
            select2Popup.Placement = PlacementMode.Bottom;
            select2Popup.AllowsTransparency = true;
            select2Popup.PopupAnimation = PopupAnimation.Slide;
            select2Popup.Focusable = false;

            Binding widthBinding = new Binding("ActualWidth") { ElementName = PartSelect2 };
            select2Popup.SetBinding(FrameworkElement.WidthProperty, widthBinding);
            return select2Popup;
        }
        #endregion

        #region "Nested Types"

        private class SuggestionsAdapter
        {

            #region "Fields"

            private readonly AutoCompleteTextBox _actb;

            private string _filter;
            #endregion

            #region "Constructors"

            public SuggestionsAdapter(AutoCompleteTextBox actb)
            {
                _actb = actb;
            }

            #endregion

            #region "Methods"

            public void GetSuggestions(string searchText)
            {
                _filter = searchText;
                _actb.IsLoading = true;
                _actb.IsDropDownOpen = true;
                _actb.ItemsSelector.ItemsSource = null;
                ParameterizedThreadStart thInfo = GetSuggestionsAsync;
                Thread th = new Thread(thInfo);
                th.Start(new object[] {
                searchText,
                _actb.Provider
            });
            }

            private void DisplaySuggestions(IEnumerable suggestions, string filter)
            {
                if (_filter != filter)
                {
                    return;
                }
                if (_actb.IsDropDownOpen)
                {
                    _actb.IsLoading = false;
                    _actb.ItemsSelector.ItemsSource = suggestions;
                    var interfaceType = typeof(IAutoCompleteItem);
                    _actb.ItemsSelector.DisplayMemberPath = interfaceType.GetProperties().FirstOrDefault().Name;
                    _actb.IsDropDownOpen = _actb.ItemsSelector.HasItems;
                }

            }

            private void GetSuggestionsAsync(object param)
            {
                if (param is object[] args)
                {
                    string searchText = Convert.ToString(args[0]);
                    if (args[1] is ISuggestionProvider provider)
                    {
                        IEnumerable list = provider.GetSuggestions(searchText);
                        _actb.Dispatcher.BeginInvoke(new Action<IEnumerable, string>(DisplaySuggestions), DispatcherPriority.Background, list, searchText);
                    }
                }
            }

            #endregion

        }

        #endregion

    }

}

这就是我在 XAML

中的调用方式
<ac:AutoCompleteTextBox x:Name="searchFloors" Tag="Floor" IsSelect2="True" Style="{StaticResource Select2Style}" DisplayMember="SearchText" SelectedItem="{Binding Floor,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

这是我的模型属性

public FloorViewModel FloorModel
{
    get { return floorModel; }
    set
    {
        floorModel = value;
        // Call OnPropertyChanged whenever the property is updated
        OnPropertyChanged("FloorModel");
    }
}

第一次更新

这只发生在两种情况下

1) 在运行时创建的 Window。

Window window = new Window();
window.Content = UpdateShelf(MainWindow, Shelf, GridList.ResultList)

2) 使用 ContentControl 时。

Xaml:

<ContentControl x:Name="updateControl"/>

代码隐藏

 var updateUC = new UpdateShelf(MainWindow, Shelf, GridList.ResultList);
 ShowUpdateUC();
 updateControl.Content = updateUC;
 updateUC.GoBack += UpdateGoBack_Event;

更新工具架是一个用户控件。为了简单起见,我编写了 UpdateShelf 构造函数,如下所示:

public UpdateShelf(MainDashboard MainWindow, ShelfViewModel shelf, ObservableCollection<ShelfViewModel> gridList)
{
        InitializeComponent();
        shelfViewModel = shelf ?? new ShelfViewModel();
        shelfViewModel.FloorModel = new FloorViewModel(){Name="Floor One"};
        DataContext = shelfViewModel;
}

第二次更新

如果我将 Datacontext 从 Constructor 移除到 Loaded Function。它按预期工作,但为什么会发生这种情况,在加载的函数中设置数据上下文是否更好?

public UpdateShelf(MainDashboard MainWindow, ShelfViewModel shelf, ObservableCollection<ShelfViewModel> gridList)
{
        InitializeComponent();
        shelfViewModel = shelf ?? new ShelfViewModel();
        shelfViewModel.FloorModel = new FloorViewModel(){Name="Floor One"};
         #region[Events Registrations]
         Loaded += new RoutedEventHandler(UpdateShelfView_Loaded);
         Unloaded += new RoutedEventHandler(UpdateShelfView_UnLoaded);
         #endregion
}

void UpdateShelfView_Loaded(object sender, RoutedEventArgs e)
{
    DataContext = shelfViewModel;
}

创建一个 Initialize() 方法来执行控件按预期执行所需的初始化操作(例如,从此处调用回调)。从您的 OnApplyTemplate 覆盖或 FrameworkElement.Loaded 事件处理程序调用此方法:

public class AutoCompleteTextBox : Control
{ 
    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
    "SelectedItem",
    typeof(object),
    typeof(AutoCompleteTextBox),
    new FrameworkPropertyMetadata(null, OnSelectedItemChanged));

  public object SelectedItem { get => (object)GetValue(AutoCompleteTextBox.SelectedItemProperty); set => SetValue(AutoCompleteTextBox.SelectedItemProperty, value);  }

  private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    ...
  }

  private void Initialize()
  {
    AutoCompleteTextBox.OnSelectedItemChanged(this, new DependencyPropertyChangedEventArgs(AutoCompleteTextBox.SelectedItemProperty, this.SelectedItem, this.SelectedItem));
  }    

  public AutoCompleteTextBox()
  {
    InitializeComponent(); 
    this.Loaded  += () => Initialize();
  }
}