0

I am working on a custom control called select 2 inspired by WPF Auto Complete. The problem is that my OnSelectionChanged (binded with a Dependecy Property called SelectedItem) is called before OnApplyTemplate (all controls are initialized for the custom control). Because of which in updation mode the Item is not gettiing selected as OnApplyTemplate was

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

    }

}

This is how I am calling it in my XAML

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

Here is my model Property

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

FIRST UPDATE

This only happens in two scenarios

1) A Window that is created on runtime.

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

2) When ContentControl is used.

Xaml:

<ContentControl x:Name="updateControl"/>

Code Behind

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

Update Shelf is a user Control. Just for the sake of simplicity I have coded UpdateShelf Constructor Like this:

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

SECOND UPDATE

If i remove Datacontext from Constructor to Loaded Function. It works as expected, but why is this happening and is it better to set datacontext in 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;
}
Safi Mustafa
  • 515
  • 7
  • 22
  • What method gets called when and when you you expect it to get called...? – mm8 Sep 20 '19 at 08:36
  • @mm8 I understand that dependency property callbacks get called before OnApplyTemplate. But What am I suppose to do to call it after OnApplyTemplate. As in Update Mode Value should be selected by default. – Safi Mustafa Sep 20 '19 at 08:49
  • A callback gets invoked when the property changes. Why don't you set the default value when you register the property? – mm8 Sep 20 '19 at 08:50
  • @mm8 This control is working perfectly for everything Except for the first time when a value is set from the database (In The Edit Mode). At that time Property change is called on databind. Afterwards it wont be called – Safi Mustafa Sep 20 '19 at 08:53
  • @mm8 This is a hacky ways of doing this: https://stackoverflow.com/questions/1298898/wpf-customcontrol-onapplytemplate-called-after-propertychangedcallback – Safi Mustafa Sep 20 '19 at 08:55

1 Answers1

1

Create a Initialize() method that performs the initialization operations required for the control to perform as intended (e.g. invoke the callback from here). Call this method from your OnApplyTemplate override or FrameworkElement.Loaded event handler:

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();
  }
}
BionicCode
  • 1
  • 4
  • 28
  • 44
  • I just found out the current Code works well until I use this control in a Window created at run time. Please check the Updated Question – Safi Mustafa Sep 20 '19 at 10:46
  • What is `UpdateFloor`? If it is a `Control` make sure you call `InitializeComponent()` from the `UpdateFloor` constructor. – BionicCode Sep 20 '19 at 10:47
  • I Have Added Code for that as well. – Safi Mustafa Sep 20 '19 at 10:53
  • Then what _exactly_ isn't working? You said the property changed callback is invoked before the `OnApplyTemplate`. And then? Please describe the behavior you expect and what is actually happening. – BionicCode Sep 20 '19 at 12:15
  • Also have you tried my suggestion? You have to trigger initialization from `OnApplyTemplate` override. Don't rely on the `PropertyChanged` callback as you yourself realized that the original order of invocation doesn't work for you... – BionicCode Sep 20 '19 at 12:18
  • Moving DataContext to Loaded function did the trick for me in Control Content or Dynamic window. Is it alright if I set my Data Context in Loaded Function? – Safi Mustafa Sep 20 '19 at 12:21
  • Yes it is ok. Since you bind to the `DataContext` all bindings will update once the `Control` is loaded. Callbacks that are then invoked will have access to a fully initialized `Control`. That's why I told you to invoke a `Initialize()` method _after_ the template was applied (or alternatively the `Control` was `Loaded`). – BionicCode Sep 20 '19 at 12:25