@@ -63,6 +63,7 @@ public MultiSelectionComboBox()
6363 private ListBox ? PART_SelectedItemsPresenter ;
6464
6565 private bool isUserdefinedTextInputPending ;
66+ private bool isTextChanging ; // This flag indicates if the text is changed by us, so we don't want to refire the TextChangedEvent.
6667 private bool shouldDoTextReset ; // Defines if the Text should be reset after selecting items from string
6768 private bool shouldAddItems ; // Defines if the MSCB should add new items from text input. Don't set this to true while input is pending. We cannot know how long the user needs for typing.
6869 private bool IsSyncingSelectedItems ; // true if syncing in one or the other direction already running
@@ -553,14 +554,17 @@ public void ResetEditableText()
553554 /// <summary>
554555 /// Updates the Text of the editable TextBox.
555556 /// Sets the custom Text if any otherwise the concatenated string.
556- /// </summary>
557+ /// </summary>
558+
557559 private void UpdateEditableText ( bool forceUpdate = false )
558560 {
559561 if ( this . PART_EditableTextBox is null || ( this . PART_EditableTextBox . IsKeyboardFocused && ! forceUpdate ) )
560562 {
561563 return ;
562564 }
563565
566+ this . isTextChanging = true ;
567+
564568 var oldSelectionStart = this . PART_EditableTextBox . SelectionStart ;
565569 var oldSelectionLength = this . PART_EditableTextBox . SelectionLength ;
566570 var oldTextLength = this . PART_EditableTextBox . Text . Length ;
@@ -588,6 +592,8 @@ private void UpdateEditableText(bool forceUpdate = false)
588592 this . PART_EditableTextBox . SelectionStart = oldSelectionStart ;
589593 this . PART_EditableTextBox . SelectionLength = oldSelectionLength ;
590594 }
595+
596+ this . isTextChanging = false ;
591597 }
592598
593599 private void UpdateDisplaySelectedItems ( )
@@ -654,7 +660,7 @@ private void UpdateDisplaySelectedItems(SelectedItemsOrderType selectedItemsOrde
654660
655661 private void SelectItemsFromText ( int millisecondsToWait )
656662 {
657- if ( ! this . isUserdefinedTextInputPending )
663+ if ( ! this . isUserdefinedTextInputPending || this . isTextChanging )
658664 {
659665 return ;
660666 }
@@ -746,10 +752,16 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
746752
747753 case SelectionMode . Multiple :
748754 case SelectionMode . Extended :
755+ if ( this . SelectedItems is null )
756+ {
757+ break ; // Exit here if we have no SelectedItems yet
758+ }
759+
749760 var strings = ! string . IsNullOrEmpty ( this . Separator ) ? this . Text ? . Split ( new [ ] { this . Separator } , StringSplitOptions . RemoveEmptyEntries ) : null ;
750761
751- // clear items here, so we can use the text before.
752- this . SelectedItems ? . Clear ( ) ;
762+ int position = 0 ;
763+
764+ // this.SelectedItems?.Clear();
753765
754766 if ( strings is not null )
755767 {
@@ -762,8 +774,27 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
762774 {
763775 if ( this . ObjectToStringComparer . CheckIfStringMatchesObject ( stringObject , item , this . EditableTextStringComparision , this . SelectedItemStringFormat ) )
764776 {
765- this . SelectedItems ? . Add ( item ) ;
766- foundItem = true ;
777+ var oldPosition = this . SelectedItems . IndexOf ( item ) ;
778+
779+ if ( oldPosition < 0 ) // if old index is <0 the item is not in list yet. let's add it.
780+ {
781+ this . SelectedItems . Insert ( position , item ) ;
782+ foundItem = true ;
783+ position ++ ;
784+ }
785+ else if ( oldPosition > position ) // if the item has a wrong position in list we need to swap it accordingly.
786+ {
787+ this . SelectedItems . RemoveAt ( oldPosition ) ;
788+ this . SelectedItems . Insert ( position , item ) ;
789+
790+ foundItem = true ;
791+ position ++ ;
792+ }
793+ else if ( oldPosition == position )
794+ {
795+ foundItem = true ;
796+ position ++ ;
797+ }
767798 }
768799 }
769800 }
@@ -772,7 +803,8 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
772803 {
773804 if ( this . shouldAddItems && this . TryAddObjectFromString ( stringObject , out var result ) )
774805 {
775- this . SelectedItems ? . Add ( result ) ;
806+ this . SelectedItems . Insert ( position , result ) ;
807+ position ++ ;
776808 }
777809 else
778810 {
@@ -782,6 +814,12 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
782814 }
783815 }
784816
817+ // Remove Items if needed.
818+ while ( this . SelectedItems . Count > position )
819+ {
820+ this . SelectedItems . RemoveAt ( position ) ;
821+ }
822+
785823 break ;
786824
787825 default :
@@ -865,9 +903,9 @@ private bool TryAddObjectFromString(string? input, out object? result)
865903 }
866904 }
867905
868- #endregion
906+ #endregion
869907
870- #region Commands
908+ #region Commands
871909
872910 // Clear Text Command
873911 public static RoutedUICommand ClearContentCommand { get ; } = new RoutedUICommand ( "ClearContent" , nameof ( ClearContentCommand ) , typeof ( MultiSelectionComboBox ) ) ;
@@ -935,9 +973,9 @@ private static void RemoveItemCommand_CanExecute(object sender, CanExecuteRouted
935973 }
936974 }
937975
938- #endregion
976+ #endregion
939977
940- #region Overrides
978+ #region Overrides
941979
942980 public override void OnApplyTemplate ( )
943981 {
@@ -966,6 +1004,8 @@ public override void OnApplyTemplate()
9661004 {
9671005 selectedItemsCollection . CollectionChanged -= this . PART_PopupListBox_SelectedItems_CollectionChanged ;
9681006 selectedItemsCollection . CollectionChanged += this . PART_PopupListBox_SelectedItems_CollectionChanged ;
1007+
1008+ PART_PopupListBox . Unloaded += ( s , e ) => { selectedItemsCollection . CollectionChanged -= this . PART_PopupListBox_SelectedItems_CollectionChanged ; } ;
9691009 }
9701010
9711011 // Do update the text
@@ -985,8 +1025,8 @@ private void PART_PopupListBox_SelectedItems_CollectionChanged(object sender, No
9851025 protected override void OnSelectionChanged ( SelectionChangedEventArgs e )
9861026 {
9871027 base . OnSelectionChanged ( e ) ;
988- this . UpdateEditableText ( ) ;
9891028 this . UpdateDisplaySelectedItems ( ) ;
1029+ this . UpdateEditableText ( ) ;
9901030 }
9911031
9921032 private void MultiSelectionComboBox_Loaded ( object sender , EventArgs e )
@@ -1112,7 +1152,7 @@ private void MoveFocusToDropDown()
11121152 }
11131153
11141154 var index = this . PART_PopupListBox . SelectedIndex ;
1115- if ( index < 0 )
1155+ if ( index < 0 && this . PART_PopupListBox . Items . Count > 0 )
11161156 {
11171157 index = 0 ;
11181158 }
@@ -1527,16 +1567,21 @@ private bool IsSelectableHelper(object o)
15271567 return ( bool ) d . GetValue ( IsEnabledProperty ) ;
15281568 }
15291569
1530- #endregion
1570+ #endregion
15311571
1532- #region Events
1572+ #region Events
15331573
15341574#if NET5_0_OR_GREATER
15351575 private void SelectedItemsImpl_CollectionChanged ( object ? sender , NotifyCollectionChangedEventArgs e )
15361576#else
15371577 private void SelectedItemsImpl_CollectionChanged ( object sender , NotifyCollectionChangedEventArgs e )
15381578#endif
15391579 {
1580+ if ( this . PART_PopupListBox is null )
1581+ {
1582+ this . ApplyTemplate ( ) ;
1583+ }
1584+
15401585 this . SyncSelectedItems ( sender as IList , this . PART_PopupListBox ? . SelectedItems , e ) ;
15411586 }
15421587
@@ -1672,7 +1717,7 @@ private static void UpdateText(DependencyObject d, DependencyPropertyChangedEven
16721717
16731718 private static void OnTextChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
16741719 {
1675- if ( d is MultiSelectionComboBox multiSelectionComboBox )
1720+ if ( d is MultiSelectionComboBox multiSelectionComboBox && ! multiSelectionComboBox . isTextChanging )
16761721 {
16771722 multiSelectionComboBox . UpdateHasCustomText ( null ) ;
16781723 multiSelectionComboBox . isUserdefinedTextInputPending = true ;
@@ -1720,6 +1765,6 @@ public event AddedItemEventArgsHandler AddedItem
17201765 remove => this . RemoveHandler ( AddedItemEvent , value ) ;
17211766 }
17221767
1723- #endregion
1768+ #endregion
17241769 }
17251770}
0 commit comments