Skip to content

Menu etc. with Selectors are broken - Formalize/Fix Command propagation #4473

@tig

Description

@tig

Summary

We currently have what I call command propagation for Command.Accept in place today where RaiseAccepting special cases any View with IsDefaultView (e.g. Button) and if not handled, propagates the command up the View hierarchy until a SuperView handles the command. This is the root of why the Accepting event handlers must indicate with e.Handled = true if they handled the command. The reason we have this is to support both default handling (e.g. Button.IsDefault) and to allow compound views such as Shortcut to be able to handle and override the behaviors of what subviews do when the user "accepts" them.

The current code in View.RaiseAccepting (in View.Command.cs):

        // Accept is a special case where if the event is not handled above, the event is
        //  - Invoked on any peer-View with IsDefault == true
        //  - bubbled up the SuperView hierarchy.
        if (!args.Handled)
        {
            // If there's an IsDefault peer view in SubViews, try it
            View? isDefaultView = SuperView?.InternalSubViews.FirstOrDefault (v => v is Button { IsDefault: true });

            if (isDefaultView != this && isDefaultView is Button { IsDefault: true } button)
            {
                Logging.Debug ($"{Title} ({ctx?.Source?.Title}) - InvokeCommand on Default View ({isDefaultView.Title})");
                bool? handled = isDefaultView.InvokeCommand (Command.Accept, ctx);

                if (handled == true)
                {
                    return true;
                }
            }

            if (SuperView is { })
            {
                return SuperView?.InvokeCommand (Command.Accept, ctx);
            }
        }
...

Note: We just renamed Select to Activate because:

  • Representing both state changes (e.g., toggling a CheckBox, selecting a ListView item) and preparatory actions (e.g., focusing a Button, navigating a MenuItemv2).
  • Distinguishing clearly from Accepting, which confirms actions (e.g., executing a menu command, submitting a dialog).
  • Supporting a targeted propagation model with PropagateActivating, enabling superviews like MenuBar to opt-in to subview Activating events without coupling subviews to superview details.

Problems

  • This code one-offs Command.Accept
  • Command.Accept is actually not the right command for this in many cases. Support for Activating (previously Selecting) is required too.
  • It puts the onus of specifying behavior on subviews, requiring them to know their superview behavior.

MenuBar requires Activating events from subviews to manage PopoverMenu visibility, but the current local handling model relies on view-specific events (e.g., SelectedMenuItemChanged), which isn’t generalizable. Subviews should remain decoupled from superviews, avoiding knowledge of superview implementation details (e.g., MenuBar’s popover logic).

Proposed Sollution

A more general and targeted propagation model for Command.Accept and Command.Activate is needed to support hierarchical components like MenuBarv, which requires Activating events from subviews (e.g., MenuItem) to manage PopoverMenu visibility. To maintain decoupling, subviews should not specify propagation behavior. Instead, superviews should opt-in to receive propagated Activating events via a new, to be designed, mechanism.

This Issue is to design that mechanism and implement it.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

Status

🏗 Approved - In progress

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions