lepoco / wpfui

WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.
https://wpfui.lepo.co
MIT License
7.4k stars 717 forks source link

Cannot add Navigation view items during Runtime #907

Open misakieku opened 8 months ago

misakieku commented 8 months ago

Describe the bug

I am trying to add new Items based on configuration after the user open the app but the issue is I cannot see that the item is been added to the navigation view. The code I have now:

            var item = AppDataUtilities.AddAppItem(appData);
            navigationView.MenuItems.Add(item);
            navigationView.UpdateLayout();

To Reproduce

Add NavigationViewItem during runtime, like event.

Expected behavior

NavigationView able to react the changes.

Screenshots

No response

OS version

Windows 11 VS 2022

.NET version

.Net 8

WPF-UI NuGet version

3.0.0 preview4

Additional context

No response

EkinBarisC commented 8 months ago

In Wpf.Ui.Controls.NavigationView source code, there is an update menu items template function. You can manually update it by doing the same for loop after adding your items in your code. just copy the for loop and adjust according to your variables. This is how i solved it at least.

protected virtual void UpdateMenuItemsTemplate(IList list) { for (int i = 0; i < list.Count; i++) { if (list[i] is NavigationViewItem navigationViewItem && ItemTemplate != null && navigationViewItem.Template != ItemTemplate) { navigationViewItem.Template = ItemTemplate; } } }

misakieku commented 8 months ago

In Wpf.Ui.Controls.NavigationView source code, there is an update menu items template function. You can manually update it by doing the same for loop after adding your items in your code. just copy the for loop and adjust according to your variables. This is how i solved it at least.

protected virtual void UpdateMenuItemsTemplate(IList list) { for (int i = 0; i < list.Count; i++) { if (list[i] is NavigationViewItem navigationViewItem && ItemTemplate != null && navigationViewItem.Template != ItemTemplate) { navigationViewItem.Template = ItemTemplate; } } }

It did work. However, OnNavigationSelectionChanged will not be trigger if I select the new item that I added.

misakieku commented 8 months ago

And this seams not working if I add the item as a subitem of another navigation view item.

EkinBarisC commented 8 months ago

You are right. I made a workaround by recreating the navigation view at every update and then re initialize it in the xaml. I also add the selection changed events which triggers it successfully. This works for me but it is not a good way of updating it at all.

` var newNavigationView = new NavigationView { MenuItemsSource = MenuItems, Header = NavigationView.Header, IsPaneOpen = NavigationView.IsPaneOpen, PaneDisplayMode = NavigationView.PaneDisplayMode, };

newNavigationView.SelectionChanged += NavigationView_SelectionChanged; var parent = NavigationView.Parent as Panel; if (parent != null) { int index = parent.Children.IndexOf(NavigationView); parent.Children.RemoveAt(index); parent.Children.Insert(index, newNavigationView); }

NavigationService.SetNavigationControl(newNavigationView);

NavigationView = newNavigationView;`

misakieku commented 8 months ago

You are right. I made a workaround by recreating the navigation view at every update and then re initialize it in the xaml. I also add the selection changed events which triggers it successfully. This works for me but it is not a good way of updating it at all.

` var newNavigationView = new NavigationView { MenuItemsSource = MenuItems, Header = NavigationView.Header, IsPaneOpen = NavigationView.IsPaneOpen, PaneDisplayMode = NavigationView.PaneDisplayMode, };

newNavigationView.SelectionChanged += NavigationView_SelectionChanged; var parent = NavigationView.Parent as Panel; if (parent != null) { int index = parent.Children.IndexOf(NavigationView); parent.Children.RemoveAt(index); parent.Children.Insert(index, newNavigationView); }

NavigationService.SetNavigationControl(newNavigationView);

NavigationView = newNavigationView;`

Thanks a lot.

sbaskoy commented 7 months ago

after some research I found the following solution

I change the Visible value instead of adding and removing the menu item


      <ui:NavigationView x:Name="NavigationView" Grid.Row="1" Padding="10,0,10,0"
              BreadcrumbBar="{Binding ElementName=BreadcrumbBar}"
              FooterMenuItemsSource="{Binding ViewModel.FooterMenuItems, Mode=OneWay}" FrameMargin="0"
              IsBackButtonVisible="Visible" IsPaneToggleVisible="True" PaneDisplayMode="LeftFluent">
          <ui:NavigationView.Header>
              <ui:BreadcrumbBar x:Name="BreadcrumbBar" Margin="10,5,10,5" />
          </ui:NavigationView.Header>
          <ui:NavigationView.ContentOverlay>
              <Grid>
                  <ui:SnackbarPresenter x:Name="SnackbarPresenter" />
              </Grid>
          </ui:NavigationView.ContentOverlay>

          <ui:NavigationView.MenuItems>
              <ui:NavigationViewItem Content="Devices" TargetPageType="{x:Type pages:DevicesPage}">
                  <ui:NavigationViewItem.Icon>
                      <ui:SymbolIcon Symbol="DeviceEq20" />
                  </ui:NavigationViewItem.Icon>
              </ui:NavigationViewItem>

              <ui:NavigationViewItem Content="Users" TargetPageType="{x:Type pages:UsersPage}">
                  <ui:NavigationViewItem.Icon>
                      <ui:SymbolIcon Symbol="People20" />
                  </ui:NavigationViewItem.Icon>
              </ui:NavigationViewItem>

              <ui:NavigationViewItem Content="Account" TargetPageType="{x:Type pages:AccountPage}">
                  <ui:NavigationViewItem.Icon>
                      <ui:SymbolIcon Symbol="NotepadPerson20" />
                  </ui:NavigationViewItem.Icon>
              </ui:NavigationViewItem>
              <!--look here trick is here-->
              <ui:NavigationViewItem Content="Connected" TargetPageType="{x:Type pages:SettingsPage}"

                                     Visibility="{Binding ViewModel.ShowConnectedTab, Converter={StaticResource BoolToVisibleConvertor}}"
                                     >
                  <ui:NavigationViewItem.Icon>
                      <ui:SymbolIcon Symbol="NotepadPerson20" />
                  </ui:NavigationViewItem.Icon>
              </ui:NavigationViewItem>

          </ui:NavigationView.MenuItems>

      </ui:NavigationView>

And my view model

   public partial class MainWindowViewModel : ObservableObject {

        [ObservableProperty]
        private string _applicationTitle = "Modibus Client";

        [ObservableProperty]
        private bool _showConnectedTab = false;

        [ObservableProperty]
        private ObservableCollection<object> _footerMenuItems = new()
        {
            new NavigationViewItem()
            {
                Content = "Settings",
                Icon = new SymbolIcon { Symbol = SymbolRegular.Settings20 },
                TargetPageType = typeof(Views.Pages.SettingsPage)
            },
              new NavigationViewItem()
            {
                Content = "Logout",
                Icon = new SymbolIcon { Symbol = SymbolRegular.ArrowExit20 },
                TargetPageType = typeof(Views.Pages.SettingsPage)
            },

        };

        [ObservableProperty]
        public string _deviceName = "device name";

        [ObservableProperty]
        public string _location = "location";

        // LOOK
        [RelayCommand]
        public void OnShowConnectedTab() {
            ShowConnectedTab = true;
        }    
        [RelayCommand]
        public void OnHideConnectedTab() {
            ShowConnectedTab = false;
        }

    }

and my converter class

internal class BoolToVisibleConvertor : IValueConverter {
    public object Convert(object value,Type targetType,object parameter,CultureInfo culture) {
       return (bool)value  ? Visibility.Visible : Visibility.Hidden;
    }

    public object ConvertBack(object value,Type targetType,object parameter,CultureInfo culture) {
        throw new NotImplementedException();
    }
}
scanfing commented 6 months ago

hi, Wpf.Ui.Controls.NavigationView source code NavigationView.Navigation.cs line: 175 - 213 - 219

 if (SelectedItem != NavigationStack[0] && NavigationStack[0].IsMenuElement)
 {
     SelectedItem = NavigationStack[0];
     OnSelectionChanged();
 }

if your item is NavigationViewItem , try :
var item = AppDataUtilities.AddAppItem(appData); item.Template = navigationView.ItemTemplate; item.IsMenuElement = true; navigationView.MenuItems.Add(item);

good luck!

zhouxinmail commented 6 months ago

I also have the same problem, but I hope to change the item by changing the item of ObservableCollection

public partial class MainWindow : IWindow
{
    public MainWindowViewModel ViewModel
    {
        get;
    }
    public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
    {

        ViewModel = viewModel;
        // ViewModel.MenuItems = ViewModel.GetMenuItemsByAuth();//with navigation
        this.DataContext = this;

        viewModel.ApplicationTitle = "111111111";

        InitializeComponent();

        this.Loaded += MainWindow_Loaded;

        navigationService.SetNavigationControl(NavigationView);

        NavigationView.SetServiceProvider(serviceProvider);
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        //ViewModel.MenuItems=  ViewModel.GetMenuItemsByAuth();//no Navigation
       // NavigationView.MenuItems = ViewModel.GetMenuItemsByAuth();// no Navigation
       // ViewModel.MenuItems.Add(new NavigationViewItem("11", SymbolRegular.Grid20, typeof(TestPage)));// no Navigation
        NavigationView.MenuItems.Add(new NavigationViewItem("11", SymbolRegular.Grid20, typeof(TestPage)));// no Navigation
    }
happygb21-jc commented 6 months ago

hi, Wpf.Ui.Controls.NavigationView source code NavigationView.Navigation.cs line: 175 - 213 - 219

 if (SelectedItem != NavigationStack[0] && NavigationStack[0].IsMenuElement)
 {
     SelectedItem = NavigationStack[0];
     OnSelectionChanged();
 }

if your item is NavigationViewItem , try : var item = AppDataUtilities.AddAppItem(appData); item.Template = navigationView.ItemTemplate; item.IsMenuElement = true; navigationView.MenuItems.Add(item);

good luck!

Thanks, I also encountered this problem. Thank you for your answer

happygb21-jc commented 6 months ago

我也有同样的问题,但我希望通过更改ObservableCollection的项目来更改项目

public partial class MainWindow : IWindow
{
    public MainWindowViewModel ViewModel
    {
        get;
    }
    public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
    {

        ViewModel = viewModel;
        // ViewModel.MenuItems = ViewModel.GetMenuItemsByAuth();//with navigation
        this.DataContext = this;

        viewModel.ApplicationTitle = "111111111";

        InitializeComponent();

        this.Loaded += MainWindow_Loaded;

        navigationService.SetNavigationControl(NavigationView);

        NavigationView.SetServiceProvider(serviceProvider);
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        //ViewModel.MenuItems=  ViewModel.GetMenuItemsByAuth();//no Navigation
       // NavigationView.MenuItems = ViewModel.GetMenuItemsByAuth();// no Navigation
       // ViewModel.MenuItems.Add(new NavigationViewItem("11", SymbolRegular.Grid20, typeof(TestPage)));// no Navigation
        NavigationView.MenuItems.Add(new NavigationViewItem("11", SymbolRegular.Grid20, typeof(TestPage)));// no Navigation
    }

You can obtain the itemTemplate in navigationService navigationViewItem.Template = navigationService.GetNavigationControl().ItemTemplate; You can try it

zhouxinmail commented 6 months ago

But the indentation of the submenu is incorrect

scanfing commented 6 months ago

But the indentation of the submenu is incorrect

base on: https://github.com/lepoco/wpfui/pull/964/files

image

image

You can refer to the above content about ObservableCollection.