microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.27k stars 674 forks source link

Proposal: Update NavigationView to support command binding #944

Open vgromfeld opened 5 years ago

vgromfeld commented 5 years ago

Proposal: Update NavigationView to support command binding

Summary

NavigationView is an useful navigation control but it does not expose any command properties to easily bind the back button, navigation item or settings button action to ICommand.

Rationale

XAML has been designed with the MVVM pattern in mind and rely a lot on binding to allow the view models to drive the view. The most common way to bind actions is to use objects implenting ICommand.

This pattern is available in (almost) all the available relevant controls (buttons, command bars...). If we want to use NavigationView we have no other choice than to write some custom code behind to route the NavigationView raw event to view models commands.

This creates an unnecessary work that can be handled by the compiler ({x:Bind}) or the runtime ({Binding}) (as for all the other buttons).

Scope

Capability Priority
Expose a NavigationView.BackCommand to bind a custom ICommand on the BackRequested event Must
Expose a NavigationView.SettingsCommand to bind a custom ICommand on the ItemInvoked(IsSettings=true) event Must
Expose a NavigationViewItem.Command on the items to bind a custom ICommand when a navigation item is invoked` Must
Use the ICommand.CanExecute() returned value from NavigationView.BackCommand to drive the back button enabled state Should
Use the ICommand.CanExecute() returned value from NavigationView.SettingsCommand to drive the settings button enabled state Should

Important Notes

With this proposal, the usage could become:

<mux:NavigationView
    PaneDisplayMode="Top" 
    BackCommand="{x:Bind ViewModel.NavigateBack}"
    SettingsCommand="{x:Bind ViewModel.OpenSettingsPage}">
    <mux:NavigationView.MenuItems>
        <mux:NavigationViewItem 
            Content="Item A"
            Command="{x:Bind ViewModel.OpenItemAPage}"/>
        <mux:NavigationViewItem 
            Content="Item B"
            Command="{x:Bind ViewModel.OpenItemBPage}"/>
    </mux:NavigationView.MenuItems>
</mux:NavigationView>

This would avoid us writing code like the following one where we need to reconciliate the invoked item and its command using some error prone code.

private void OnNavigationItemInvoked(Mux.NavigationView sender, Mux.NavigationViewItemInvokedEventArgs args)
{
    if (args.IsSettingsInvoked)
    {
        ViewModel.OpenSettingsPage.Execute(null);
    }
    else if (args.InvokedItemContainer == openItemANavigationViewItem)
    {
        ViewModel.OpenItemAPage.Execute(null);
    }
    else if (args.InvokedItemContainer == openItemBNavigationViewItem)
    {
        ViewModel.OpenItemAPage.Execute(null);
    }
    // a lots of other lines
}
YuliKl commented 5 years ago

Conceptually, NavigationView was designed to hold navigation destinations (usually pages within an app) rather than traditional commands (like Save, Open, Copy). The Back button is different from Settings and other NavigationViewItems in that its purpose is to issue a "navigate backwards in the stack" command.

Your sample code is implying that nav view items all have a "navigate to page x" command. So this seems like a good feature request.

Shad0wlife commented 3 years ago

Sorry for the necro, but I was just looking for this feature. I'd also add that if a Command capability is added, a CommandParameter should be usable as well. For me, the commands would be especially useful in the footer, eg. If I want to have a print Item there, that needs to do more work than just change the selection maybe. A command would feel cleaner than using the "Tapped"-Event in MVVM.

Grinch commented 3 years ago

Would love to see this change request implemented.

DRAirey1 commented 2 years ago

Conceptually, NavigationView was designed to hold navigation destinations (usually pages within an app) rather than traditional commands (like Save, Open, Copy).

This is one of the most short-sighted design decisions in all of UWP/WinUI. There is no difference between a 'navigation destination' and a command in MVVM. The design in the OP is the way every control should be built.

Balkoth commented 2 years ago

Just stumbled across this and i am baffled that there are still controls around that do not support command binding. Seemed natural to me that i could just bind those NavigationViewItem objects to commands in the viewmodel, but nope this is still not implemented.

markolbert commented 1 year ago

I would love to see this implemented, particularly as I just wasted over an hour trying to figure out how to do it since it's the model/approach used by most/all of the other controls :)

Ayley commented 1 year ago

My solution was to set SelectsOnInvoked to false, now it is no longer not selected when you click the element. With the event PointerPressed you can do something when the element is pressed.

image image