Search code examples
c#wpfnavigationview

Enable/Disable sub-menu item within C# WPF NavigationView control


I have c# WPF application which uses the NavigationView control to provide a menu tree. I would like to enable/disable items within the menu based on the state of the application.

My NavigationView is declared in xaml as follows

MyApp.xaml

<Window.Resources>
    <!--  Simple data template for navigation menu items  -->
    <DataTemplate x:Key="NavigationViewMenuItem">
        <ui:NavigationViewItem
            Content="{Binding Title}"
            Icon="{Binding Icon}"
            SelectsOnInvoked="{Binding Selectable}"
            FontWeight="{Binding Selectable, Converter={x:Static cnv:FontConverters.FontWeight}}"
            MenuItemsSource="{Binding Children}"/>
    </DataTemplate>

    <SolidColorBrush x:Key="NavigationViewItemForegroundSelected" Color="{StaticResource CoralMagentaLight2}"/>
    <SolidColorBrush x:Key="NavigationViewItemForegroundSelectedPointerOver" Color="{StaticResource CoralMagentaLight2}"/>
    <SolidColorBrush x:Key="NavigationViewItemForegroundSelectedPressed" Color="{StaticResource CoralMagentaLight2}"/>
</Window.Resources>
<ui:NavigationView
   x:Name="NavigationView"
   IsBackButtonVisible="Collapsed"
   MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
   PaneDisplayMode="Auto"
   md:ShadowAssist.ShadowEdges="Right">

The control is bound to a list on the view model

MyApp.xaml.cs

this.OneWayBind(ViewModel, vm => vm.MenuItems, v => v.NavigationView.MenuItemsSource)
                    .DisposeWith(disposables);

MyViewModel.cs

[Reactive]
public List<MenuItemModel> MenuItems { get; set; } = NavigationHelper.NavigationHierarchy;

I provide a method to change the selectable state of a given item as follows

public void SetMenuItemState(string title, bool enabled)
{
    var item = MenuItems.Where(x => x.Title == title).First();
    if (item != null)
    {
        if (item.Selectable != enabled)
        {
            item.Selectable = enabled;
            
            // Required to completely re-assign the MenuItems for 
            // the change to take affect.
            MenuItems = NavigationHelper.EmptyHierarchy;
            MenuItems = items;
        }
    }
}

I would like to avoid having to update the entire list for the change to take affect and by changing the menu item state in-situ, like below, however this doesn't work.

public void SetMenuItemState(string title, bool enabled)
{
    var items = MenuItems;
    var item = MenuItems.Where(x => x.Title == title).First();
    if (item != null)
    {
        int index = MenuItems.IndexOf(item);
        MenuItems[index].Selectable = enabled;
    }
}

I tried making the menu items list and observable collection, however this made no difference. I'd be grateful if someone could advise a way to disable/enable a menu item without having to re-assign the entire list, if possible.


Solution

  • Your MenuItemModel should implement INotifyPropertyChanged and raise the PropertyChanged event for the Selectable property:

    public class MenuItemModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private int _selectable;
        public bool Selectable
        {
            get { return _selectable; }
            set { _selectable = value; NotifyPropertyChanged(); }
        }
    
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        ...
    }
    

    Then your original code should work. There is no reason to re-set menuItems[index] or switch to an ObservableCollection<T>.