private void ItemsSource_PropertyChanged(DependencyObject sender, DependencyProperty dp)
 {
     // If we're given a different ItemsSource, we need to wrap that collection in our helper class.
     if (ItemsSource != null && ItemsSource.GetType() != typeof(InterspersedObservableCollection))
     {
         _innerItemsSource = new InterspersedObservableCollection(ItemsSource);
         _currentTextEdit  = _lastTextEdit = new PretokenStringContainer(true);
         _innerItemsSource.Insert(_innerItemsSource.Count, _currentTextEdit);
         ItemsSource = _innerItemsSource;
     }
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="TokenizingTextBox"/> class.
        /// </summary>
        public TokenizingTextBox()
        {
            // Setup our base state of our collection
            _innerItemsSource = new InterspersedObservableCollection(new ObservableCollection <object>()); // TODO: Test this still will let us bind to ItemsSource in XAML?
            _currentTextEdit  = _lastTextEdit = new PretokenStringContainer(true);
            _innerItemsSource.Insert(_innerItemsSource.Count, _currentTextEdit);
            ItemsSource = _innerItemsSource;
            //// TODO: Consolidate with callback below for ItemsSourceProperty changed?

            DefaultStyleKey = typeof(TokenizingTextBox);

            // TODO: Do we want to support ItemsSource better? Need to investigate how that works with adding...
            RegisterPropertyChangedCallback(ItemsSourceProperty, ItemsSource_PropertyChanged);
            PreviewKeyDown    += TokenizingTextBox_PreviewKeyDown;
            PreviewKeyUp      += TokenizingTextBox_PreviewKeyUp;
            CharacterReceived += TokenizingTextBox_CharacterReceived;
            ItemClick         += TokenizingTextBox_ItemClick;
        }
예제 #3
0
        private void ItemsSource_PropertyChanged(DependencyObject sender, DependencyProperty dp)
        {
            // If we're given a different ItemsSource, we need to wrap that collection in our helper class.
            if (ItemsSource != null && ItemsSource.GetType() != typeof(InterspersedObservableCollection))
            {
                _innerItemsSource = new InterspersedObservableCollection(ItemsSource);

                if (ReadLocalValue(MaximumTokensProperty) != DependencyProperty.UnsetValue && _innerItemsSource.ItemsSource.Count >= MaximumTokens)
                {
                    // Reduce down to below the max as necessary.
                    var endCount = MaximumTokens > 0 ? MaximumTokens : 0;
                    for (var i = _innerItemsSource.ItemsSource.Count - 1; i >= endCount; --i)
                    {
                        _innerItemsSource.Remove(_innerItemsSource[i]);
                    }
                }

                _currentTextEdit = _lastTextEdit = new PretokenStringContainer(true);
                _innerItemsSource.Insert(_innerItemsSource.Count, _currentTextEdit);
                ItemsSource = _innerItemsSource;
            }
        }
        private async void TokenizingTextBox_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args)
        {
            var container = ContainerFromItem(_currentTextEdit) as TokenizingTextBoxItem;

            if (container != null && !(GetFocusedElement().Equals(container._autoSuggestTextBox) || char.IsControl(args.Character)))
            {
                if (SelectedItems.Count > 0)
                {
                    var index = _innerItemsSource.IndexOf(SelectedItems.First());

                    await RemoveAllSelectedTokens();

                    // Wait for removal of old items
                    _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        // If we're before the last textbox and it's empty, redirect focus to that one instead
                        if (index == _innerItemsSource.Count - 1 && string.IsNullOrWhiteSpace(_lastTextEdit.Text))
                        {
                            var lastContainer = ContainerFromItem(_lastTextEdit) as TokenizingTextBoxItem;

                            lastContainer.UseCharacterAsUser = true; // Make sure we trigger a refresh of suggested items.

                            _lastTextEdit.Text = string.Empty + args.Character;

                            UpdateCurrentTextEdit(_lastTextEdit);

                            lastContainer._autoSuggestTextBox.SelectionStart = 1; // Set position to after our new character inserted

                            lastContainer._autoSuggestTextBox.Focus(FocusState.Keyboard);
                        }
                        else
                        {
                            //// Otherwise, create a new textbox for this text.

                            UpdateCurrentTextEdit(new PretokenStringContainer((string.Empty + args.Character).Trim())); // Trim so that 'space' isn't inserted and can be used to insert a new box.

                            _innerItemsSource.Insert(index, _currentTextEdit);

                            // Need to wait for containerization
                            _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                            {
                                var newContainer = ContainerFromIndex(index) as TokenizingTextBoxItem; // Should be our last text box

                                newContainer.UseCharacterAsUser = true;                                // Make sure we trigger a refresh of suggested items.

                                void WaitForLoad(object s, RoutedEventArgs eargs)
                                {
                                    if (newContainer._autoSuggestTextBox != null)
                                    {
                                        newContainer._autoSuggestTextBox.SelectionStart = 1; // Set position to after our new character inserted

                                        newContainer._autoSuggestTextBox.Focus(FocusState.Keyboard);
                                    }

                                    newContainer.Loaded -= WaitForLoad;
                                }

                                newContainer.AutoSuggestTextBoxLoaded += WaitForLoad;
                            });
                        }
                    });
                }
                else
                {
                    // TODO: It looks like we're setting selection and focus together on items? Not sure if that's what we want...
                    // If that's the case, don't think this code will ever be called?

                    //// TODO: Behavior question: if no items selected (just focus) does it just go to our last active textbox?
                    //// Community voted that typing in the end box made sense

                    if (_innerItemsSource[_innerItemsSource.Count - 1] is ITokenStringContainer textToken)
                    {
                        var last     = ContainerFromIndex(Items.Count - 1) as TokenizingTextBoxItem; // Should be our last text box
                        var position = last._autoSuggestTextBox.SelectionStart;
                        textToken.Text = last._autoSuggestTextBox.Text.Substring(0, position) + args.Character +
                                         last._autoSuggestTextBox.Text.Substring(position);

                        last._autoSuggestTextBox.SelectionStart = position + 1; // Set position to after our new character inserted

                        last._autoSuggestTextBox.Focus(FocusState.Keyboard);
                    }
                }
            }
        }
예제 #5
0
        private async void TokenizingTextBox_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args)
        {
            var container = ContainerFromItem(_currentTextEdit) as TokenizingTextBoxItem;

            if (container != null && !(GetFocusedElement().Equals(container._autoSuggestTextBox) || char.IsControl(args.Character)))
            {
                if (SelectedItems.Count > 0)
                {
                    var index = _innerItemsSource.IndexOf(SelectedItems.First());

                    await RemoveAllSelectedTokens();

                    // Wait for removal of old items
                    var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
                    _ = dispatcherQueue.EnqueueAsync(
                        () =>
                    {
                        // If we're before the last textbox and it's empty, redirect focus to that one instead
                        if (index == _innerItemsSource.Count - 1 && string.IsNullOrWhiteSpace(_lastTextEdit.Text))
                        {
                            var lastContainer = ContainerFromItem(_lastTextEdit) as TokenizingTextBoxItem;

                            lastContainer.UseCharacterAsUser = true;     // Make sure we trigger a refresh of suggested items.

                            _lastTextEdit.Text = string.Empty + args.Character;

                            UpdateCurrentTextEdit(_lastTextEdit);

                            lastContainer._autoSuggestTextBox.SelectionStart = 1;     // Set position to after our new character inserted

                            lastContainer._autoSuggestTextBox.Focus(FocusState.Keyboard);
                        }
                        else
                        {
                            //// Otherwise, create a new textbox for this text.

                            UpdateCurrentTextEdit(new PretokenStringContainer((string.Empty + args.Character).Trim()));     // Trim so that 'space' isn't inserted and can be used to insert a new box.

                            _innerItemsSource.Insert(index, _currentTextEdit);

                            // Need to wait for containerization
                            _ = dispatcherQueue.EnqueueAsync(
                                () =>
                            {
                                var newContainer = ContainerFromIndex(index) as TokenizingTextBoxItem; // Should be our last text box

                                newContainer.UseCharacterAsUser = true;                                // Make sure we trigger a refresh of suggested items.

                                void WaitForLoad(object s, RoutedEventArgs eargs)
                                {
                                    if (newContainer._autoSuggestTextBox != null)
                                    {
                                        newContainer._autoSuggestTextBox.SelectionStart = 1;         // Set position to after our new character inserted

                                        newContainer._autoSuggestTextBox.Focus(FocusState.Keyboard);
                                    }

                                    newContainer.Loaded -= WaitForLoad;
                                }

                                newContainer.AutoSuggestTextBoxLoaded += WaitForLoad;
                            }, DispatcherQueuePriority.Normal);
                        }
                    }, DispatcherQueuePriority.Normal);
                }
                else
                {
                    // If no items are selected, send input to the last active string container.
                    // This code is only fires during an edgecase where an item is in the process of being deleted and the user inputs a character before the focus has been redirected to a string container.
                    if (_innerItemsSource[_innerItemsSource.Count - 1] is ITokenStringContainer textToken)
                    {
                        var last           = ContainerFromIndex(Items.Count - 1) as TokenizingTextBoxItem; // Should be our last text box
                        var text           = last._autoSuggestTextBox.Text;
                        var selectionStart = last._autoSuggestTextBox.SelectionStart;
                        var position       = selectionStart > text.Length ? text.Length : selectionStart;
                        textToken.Text = text.Substring(0, position) + args.Character +
                                         text.Substring(position);

                        last._autoSuggestTextBox.SelectionStart = position + 1; // Set position to after our new character inserted

                        last._autoSuggestTextBox.Focus(FocusState.Keyboard);
                    }
                }
            }
        }