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

SelectingItemsControl is handling too many user interactions without options to disable #15433

Open
billhenn opened this issue Apr 18, 2024 · 5 comments

Comments

@billhenn
Copy link
Contributor

Is your feature request related to a problem? Please describe.

We are implementing a control that indirectly inherits SelectingItemsControl. Some of the items in the control are items that should be selectable, and others (such as separators) are items that should not be selectable. We have found that there are built-in user interactions in SelectingItemsControl such as focus tracking, clicking, etc. on items that trigger the event source item to be selected.

WPF's Selector class used a more manual approach for updating selection, which did allow multiple kinds of items to be displayed, while only allowing a subset to be selectable.

Describe the solution you'd like

We'd love to see the best of both worlds, where current features in SelectingItemsControl that deal with user interactivity can be retained, but add an option or options to turn them off.

Either that, or have some callback such as:

protected virtual bool CanSelect(Control child) => true

And have that determine if a child container control is selectable before automatically trying to select it.

Describe alternatives you've considered

No response

Additional context

No response

@grokys
Copy link
Member

grokys commented Apr 23, 2024

SelectingItemsControl only implements one user interaction: for IsTextSearchEnabled. That was implemented in this PR:

#6210

I wish I'd seen that PR before it got merged because SelectingItemsControl explicitly says in the XML docs for it (emphasis added):

SelectingItemsControl maintains a selection respecting the current SelectionMode but it does not react to user input; this must be handled in a derived class.

SelectingItemsControl was designed to not respond to user interaction precisely for the reasons you explain above.

However...

This is beside the point because after speaking with @billhenn privately, the IsTextSearch interaction support isn't what he's having issues with; instead the issues are coming from classes derived from SelectingItemsControl in particular TabControl. One of the problems that he's hitting is that in WPF one can override the container class to customize interaction handling, but in Avalonia one has to override the methods in the SelectingItemsControl itself in many cases.

I first plan to enumerate the differences between Avalonia and WPF in respect to where interactions are handled with regards to items controls to get an idea of where we differ.

@robloo
Copy link
Contributor

robloo commented Apr 27, 2024

One related thing I've noticed is that setting IsHitTestVisible still allows selecting items using the keyboard. There was a need for groups in a ListBox (supported in UWP/WinUI ListViews) and creating a group header with a different data template that has IsHitTestVisible=false is a good technique to implement this and prevent clicking on the header. However, there is no way to stop selection by the keyboard.

It's debatable if IsHitTestVisible=false should make a list box item invisible for keyboard selection as well but I believe WPF works this way.

@grokys
Copy link
Member

grokys commented May 16, 2024

Took a while to get around to this, sorry. Here's what handles interactions in the most common selecting items controls in Avalonia and WPF:

ComboBox

Avalonia

ComboBox: handles interactions in OnPointer* and OnKeyDown
ComboBoxItem: handles focus and notifies parent ComboBox

WPF

ComboBox: handles interactions in OnGotFocus, OnMouseWheel and PreviewTextInput
ComboBoxItem: handles interactions in OnMouse*

ListBox

Avalonia

ListBox: handles interactions in OnKeyDown
ListBoxItem: handles interactions in OnPointer*

WPF

ListBox: handles interactions in OnKeyDown
ListBoxItem: handles interactions in OnMouse*

TabControl

Avalonia

TabControl: handles interactions in OnPointer*
TabItem: no interactions

WPF

TabControl: handles interactions in OnKeyDown
TabItem: handles interactions in OnMouseLeftButtonDown, OnPreviewGotFocus, OnAccessKey

So it looks like ListBox is broadly in line with WPF but ComboBox and TabControl don't delegate input to their containers as they do in WPF.

@grokys
Copy link
Member

grokys commented May 16, 2024

My question for you now @billhenn is could you go into a bit of detail on why it's easier to customize interactions by overriding methods in the container rather than in the items control itself? One would have thought that it doesn't make much difference where these interactions are handled.

(I know you explained it to me privately but would be good for it to be of public record)

@billhenn
Copy link
Contributor Author

My question for you now @billhenn is could you go into a bit of detail on why it's easier to customize interactions by overriding methods in the container rather than in the items control itself? One would have thought that it doesn't make much difference where these interactions are handled.

Sure @grokys, someone building a primitive ItemsControl might assume that its item container controls are always of a certain type. For instance, the author of a TabControl might assume that only TabItem container controls are ever present. In most cases that is true.

However, for core Avalonia controls like this, there are times where things might be customized further and other container control types introduced. A specific instance of this is a ribbon "backstage" control, which we may or may not be working on. 😉 The RibbonBackstage control is effectively a tab control and therefore we inherit TabControl to obtain all the benefits of what the Avalonia team has developed for TabControl. However RibbonBackstage can also include other item container controls like buttons that are intermixed with tabs.

A problem with all interactions being in the items control itself, is that in the case of TabControl it's assuming that only tabs are present and it's trying to make focused buttons be selected items. Buttons don't make sense to be "selected" in a TabControl though, although they should be focusable and clickable. Whereas with the WPF-like design where the item container controls are the ones who determine when they should affect a parent TabControl selection (i.e., tabs do and buttons don't), we can have this kind of intermixing of new item container control types without any issues.

In summary, a TabItem control should be what tells the parent TabControl when to select itself based on pointer and focus interactions. And keyboard-based navigation should generally be handled in the TabControl. That sort of configuration, which is how WPF does things, is what we've found makes the most sense over years of building UI controls and allows for better extensibility support like what we need for RibbonBackstage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants