Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make TokenizingTextBox more flexible #256

Open
minesworld opened this issue Oct 11, 2023 · 6 comments · May be fixed by #261
Open

Make TokenizingTextBox more flexible #256

minesworld opened this issue Oct 11, 2023 · 6 comments · May be fixed by #261
Labels
need more info 📌 question Further information is requested

Comments

@minesworld
Copy link

minesworld commented Oct 11, 2023

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

@ghost
Copy link

ghost commented Oct 11, 2023

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!

@minesworld
Copy link
Author

minesworld commented Oct 11, 2023

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.

@michael-hawker michael-hawker transferred this issue from CommunityToolkit/WindowsCommunityToolkit Oct 16, 2023
@michael-hawker michael-hawker added the question Further information is requested label Oct 16, 2023
@michael-hawker
Copy link
Member

moved this to the new repository

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

public void AddTokenItem(object data, bool atEnd = false)

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!

@minesworld
Copy link
Author

minesworld commented Oct 17, 2023

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
Copy link
Author

minesworld commented Oct 17, 2023

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 added a commit to minesworld/CommunityToolkit-Windows that referenced this issue Oct 17, 2023
@minesworld minesworld linked a pull request Oct 17, 2023 that will close this issue
8 tasks
@minesworld
Copy link
Author

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need more info 📌 question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants