FluentAvalonia icon indicating copy to clipboard operation
FluentAvalonia copied to clipboard

NavigationView.SelectedItem is not updating its selection state when setting the selected item from ViewModel

Open hematec opened this issue 3 years ago • 6 comments

Hello everybody,

I've bound the NavigationView.SelectedItem to my VMs property 'SelectedModuleMenuItem'. When i change the SelectedModuleMenuItem in my VMs code, the selection in NavigationView is not updated.

Screenshots image

Desktop (please complete the following information):

  • OS: Windows 10
  • FluentAvalonia Version 1.2.1
  • Avalonia Version 0.10.12

Thx for your help!

hematec avatar Feb 10 '22 13:02 hematec

I'm unable to repro this behavior. Do you have some sample code or a minimal repro? Are you trying to use the Menu to select the NavigationView items? If so, that may be the problem, because of the way menus are implemented.

amwx avatar Feb 14 '22 23:02 amwx

Hi,

yes, u r right - i'm using a Menu to select the current NavigationViewItem. The XAML of the Menu looks like this.

<Menu Margin="0,0,0,10" DockPanel.Dock="Top" Items="{Binding MenuItems}">
  <Menu.Styles>
    <Style Selector="MenuItem">
      <Setter Property="ClipToBounds" Value="false" />
      <Setter Property="Header" Value="{Binding Text}"/>
      <Setter Property="Items" Value="{Binding Items}"/>
      <Setter Property="Command" Value="{Binding Command}"/>
      <Setter Property="CommandParameter" Value="{Binding}"/>
    </Style>
  </Menu.Styles>
</Menu>

And the NavigationMenu XAML like this:

<ui:NavigationView PaneTitle="Hauptmenu"  MenuItems="{Binding ModuleMenuItems}"  SelectedItem="{Binding SelectedModuleMenuItem}">
      <ui:NavigationView.MenuItemTemplate>
        <DataTemplate>
          <ui:NavigationViewItem Content="{Binding Text}" Icon="{Binding Icon}" ToolTip.Tip="{Binding ToolTip}" MenuItems="{Binding Items}" />
        </DataTemplate>
      </ui:NavigationView.MenuItemTemplate>
    </ui:NavigationView>

In my ViewModel the MenuItems are created as following:

ModuleMenuItems = new[] {
                    new ModuleMenuItemViewModel { Text = "Stammdaten", Items = new[] {
                        new ModuleMenuItemViewModel { Text = "Produkte", Heading="Produkte", ModuleViewModel = new ProductsViewModel(), Command = ShowModuleCommand },
                        new ModuleMenuItemViewModel { Text = "Adressen", Heading ="Adressen", ModuleViewModel = new AddressViewModel(), Command = ShowModuleCommand },
                        }
                    }

The Bound Command of the MenuItems set the SelectedModuleMenuItem which is also bound to the NavigationView SelectedItem:

private void DoShowModule(ModuleMenuItemViewModel mmivm)
{
    SelectedModuleMenuItem = mmivm;
}
ModuleMenuItemViewModel _SelectedModuleMenuItem;
public ModuleMenuItemViewModel SelectedModuleMenuItem
{
    get => _SelectedModuleMenuItem;
    set
    {
        if (value != null && value.ModuleViewModel != null 
            && (CurrentModuleViewModel == null || CurrentModuleViewModel.CanDeactivateViewModel()))
        {
            this.RaiseAndSetIfChanged(ref _SelectedModuleMenuItem, value);
            CurrentModuleViewModel = value.ModuleViewModel;
        }
    }
}

For the sake of completness here also the ModuleMenuItemViewModel class

public class ModuleMenuItemViewModel : MenuItemViewModel
{
    string _Heading;
    public string Heading { get => _Heading; internal set => this.RaiseAndSetIfChanged(ref _Heading, value); }

    ModuleViewModelBase _ModuleViewModel;
    public ModuleViewModelBase ModuleViewModel { get => _ModuleViewModel; internal set => this.RaiseAndSetIfChanged(ref _ModuleViewModel, value); }
}

BR, hematec

hematec avatar Mar 01 '22 16:03 hematec

Still unable to repro this. The code fragments were still missing some components so I had to try to piece together what you were doing - however I'm still able to successfully bind a command to a MenuItem that programmatically sets the SelectedItem in the VM and get the selection to properly update in the NavigationView.

A couple of notes... 1 - Is there a reason you need both the MenuBar and the NavigationView? They're both considered top-level navigation controls and probably don't need to be used together since they accomplish the same thing.

2 - It looks like you're using the old Fluent theme from upstream in Avalonia in conjunction with the NavigationView - which doesn't natively support the old theme since v1.1.5? when I removed support for it. It's possible there's some funny business going on there with the styles that's making this happen?

amwx avatar Mar 08 '22 04:03 amwx

I have made an example here where the problem is visible. I am not using the NavigationView quite as intended, as I want to instantiate my own view models and use Reactive UI.

Laurnz avatar Aug 27 '22 08:08 Laurnz

This is a somewhat related question. I want my view models to have constructor parameters like services. Is it possible to set the data context of the page to navigate to when using the frame control? Or is it possible that they get injected by the framework (but since System.Activator is used, I doubt it)? Is it the best practice to use optional constructor parameters, that fall back to a static reference if it is not passed through the constructor, allowing unit tests, but still has default constructors when used by Avalonia?

Laurnz avatar Aug 27 '22 08:08 Laurnz

I made an even simpler example here: https://github.com/taj-ny/FATest.

Pressing the "Navigate to page 2" button would result in a "Call from invalid thread" exception. However, I forgot to add UseReactiveUI() to BuildAvaloniaApp. So I added it and got the same issue. image

This problem seems to be somehow caused by ReactiveCommand. I replaced it with CommunityToolkit.Mvvm's RelayCommand, which is what I use in my projects, and everything works correctly.

public MainWindowViewModel()
{
-   NavigateToPage2Command = new ReactiveCommand.Create(NavigateToPage2);
+   NavigateToPage2Command = new RelayCommand(NavigateToPage2);
}

This fix also works for Laurnz's example.

taj-ny avatar Aug 27 '22 12:08 taj-ny