Windows icon indicating copy to clipboard operation
Windows copied to clipboard

Make TokenizingTextBox more flexible

Open minesworld opened this issue 2 years ago • 6 comments

Describe the problem

The current model only allows a single use case: adding text as AddTokenAsync() data has to be a string. Otherwise the TokenItemAdding event isn't raised and there is no chance to change the Item to be added.

By always raising the TokenItemAdding event the TokenizingTextBox could be used in ways not possible without.

Also needed: having the possibility to access the AutoSuggestBox IsSuggestionListOpen property...

Describe the solution

TokenizingTextBox.cs

if data is not a string, call the event with string.Empty and the item set to data... should be compatible this way with existing code.

   internal async Task AddTokenAsync(object data, bool? atEnd = null)
   {
       if (ReadLocalValue(MaximumTokensProperty) != DependencyProperty.UnsetValue && (MaximumTokens <= 0 || MaximumTokens <= _innerItemsSource.ItemsSource.Count))
       {
           // No tokens for you
           return;
       }

       if (TokenItemAdding != null)
       { 
           TokenItemAddingEventArgs tiaea;
           if (data is string str)
           {
               tiaea = new TokenItemAddingEventArgs(str);
           }
           else
           {
               tiaea = new TokenItemAddingEventArgs(string.Empty);
               tiaea.Item = data;
           }
       
           await TokenItemAdding.InvokeAsync(this, tiaea);

           if (tiaea.Cancel)
           {
               return;
           }

           if (tiaea.Item != null)
           {
               data = tiaea.Item; // Transformed by event implementor
           }
       }

       // If we've been typing in the last box, just add this to the end of our collection
       if (atEnd == true || _currentTextEdit == _lastTextEdit)
       {
           _innerItemsSource.InsertAt(_innerItemsSource.Count - 1, data);
       }
       else
       {
           // Otherwise, we'll insert before our current box
           var edit = _currentTextEdit;
           var index = _innerItemsSource.IndexOf(edit);

           // Insert our new data item at the location of our textbox
           _innerItemsSource.InsertAt(index, data);

           // Remove our textbox
           _innerItemsSource.Remove(edit);
       }

       // Focus back to our end box as Outlook does.
       var last = ContainerFromItem(_lastTextEdit) as TokenizingTextBoxItem;
       last?._autoSuggestTextBox.Focus(FocusState.Keyboard);

       TokenItemAdded?.Invoke(this, data);

       GuardAgainstPlaceholderTextLayoutIssue();
   }
    public bool IsSuggestionListOpen
    {
        get => (ContainerFromItem(_lastTextEdit) is TokenizingTextBoxItem lastContainer) ? lastContainer.IsSuggestionListOpen : false;
        set
        {
            if (ContainerFromItem(_lastTextEdit) is TokenizingTextBoxItem lastContainer) lastContainer.IsSuggestionListOpen = value;
        }
    }

TokenizingTextBoxItem.AutoSuggestBox.cs

    internal bool IsSuggestionListOpen
    {
        get => (_autoSuggestBox != null) ? _autoSuggestBox.IsSuggestionListOpen : false;
        set { if (_autoSuggestBox != null) _autoSuggestBox.IsSuggestionListOpen = value; }
    }

Alternatives

None

Additional info

Sorry for not providing a pull request.

Spent hours integrating the needed source files into my project in Visual Studio... . "total mess" for someone new to Windows development just wanting to code and having no clue about the tool chain.

Help us help you

None

minesworld avatar Oct 11 '23 09:10 minesworld

Hello, 'minesworld! Thanks for submitting a new feature request. I've automatically added a vote 👍 reaction to help get things started. Other community members can vote to help us prioritize this feature in the future!

ghost avatar Oct 11 '23 09:10 ghost

About the compability with existing code:

It would be nice to notice the users of TokenizingTextBox that they should check in their event handler for the case that the TokenItemAddingEventArgs TokenText is string.Empty and the Item is set instead...

The solution shouldn't break existing code - but better "no surprises" for sure.

minesworld avatar Oct 11 '23 09:10 minesworld

moved this to the new repository

@minesworld I'm confused, AddTokenItem allows you to directly pass in whatever data object you want already:

https://github.com/CommunityToolkit/Windows/blob/82eb9a9e066c877ff2eb31768cb2ea995f1af0b7/components/TokenizingTextBox/src/TokenizingTextBox.cs#L466

And in that case, you don't need to transform it. The call to TokenItemAdding for string is for when the user types in something directly into the box which needs to be mapped to your programmatic model within an application.

If you want to programmatically add a specific item, you can do whatever transform you need to first, and then call AddTokenItem with the resultant object of whatever type you have.

Can you elaborate on your scenario and why the above isn't sufficient? Thanks!

michael-hawker avatar Oct 16 '23 22:10 michael-hawker

Per default I'm presenting the user a List of Templates to choose from. So the choosen data should NOT be inserted. The TokenItemAdding event handler could Cancel the event, but then nothing gets inserted. Of course it can call the AddTokenItem directly or maybe using the DispatcherQueue.

Might have to check that, don't remember if I've tried AddTokenItem within the TokenItemAdding event handler - but its easier to use it this way...

minesworld avatar Oct 17 '23 06:10 minesworld

You're right. its even easier - by replacing the Token in an TokenItemAdded handler with another one using directly theTokenizingTextBox ItemsSource .

Using AddTokenItem in the TokenItemAdded event handler would raise that event again...

But : to replace an added Token with Text - setting the TokenizingTextBox Text has no effect within the TokenItemAdded handler.

To be able to do this this must be done by handling the LostFocus / GotFocus handlers and doing it there. To be sure that the new AutoSuggestBox has the focus the GetIsAutoSuggestTextBoxFocused() ( #259 ) method is needed... (i think)

The SuggestionListOpen is really needed - have made a pull request.

minesworld avatar Oct 17 '23 09:10 minesworld

I have to revise my comment. It is a lot more code not having the AddTokenAsync always fire an TokenItemAdding event. Its near impossible to archive the same desired behaviour.

I will provide within the next days (hopefully) a working example what I am doing with the TokenizingTextBox - then you might see why this call is such usefull at least to me. But I think when others see what is possible they might to want to use it too...

minesworld avatar Oct 17 '23 20:10 minesworld