Provide mechanism for navigation to be set up to properly respect MVVM
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 9.6k
- Forks
- 1k
- PR merge metrics
- No merged PRs in 30d
Description
Is your feature request related to a problem? Please describe
The navigation is very hard (impossible?) to set up in an truly MVVM segregated way.
I'll use the templates as an example, the MainWindowViewModel directly spins up NavigationItem UI controls and references the view layer of an application when providing the type of the page to present.
in a project where youhave the application, the view layer, the viewmodel layer and the model layer all in separate projects (to help enforce the layer segregation) this is impossible to do because it would create a cyclic dependency between the viewmodel layer and the view layer. The viewmodels should not be creating UI controls or referencing UI pages.
The viewmodel should hold data which the view then presents in UI elements.
Describe the solution you'd like
The way this would be better to work would be to have viewmodel representations of these items, for example
public partial class NavigationItemViewModel : ObservableObject, INavigationItemViewModel
{
[ObservableProperty]
private string _header;
[ObservableProperty]
private string _tag;
[ObservableProperty]
private IconViewModel _icon;
[ObservableProperty]
private object _content;
}
public partial class MenuItemViewModel : ObservableObject, IMenuItemViewModel
{
[ObservableProperty]
private string _header;
[ObservableProperty]
private string _tag;
[ObservableProperty]
private IconViewModel _icon;
[ObservableProperty]
private object _content;
}
public partial class IconViewModel : ObservableObject
{
[ObservableProperty]
private SymbolRegular _icon;
[ObservableProperty]
private bool _filled = false;
}
you could then instantiate something like
NavigationItems = new ObservableCollection<INavigationItemViewModel>
{
new NavigationItemViewModel()
{
Header = "Home",
Tag = "dashboard",
Icon = new IconViewModel
{
Icon = SymbolRegular.Home24
},
Content = new DashboardViewModel()
},
new NavigationItemViewModel()
{
Header = "Data",
Tag = "data",
Icon = new IconViewModel
{
Icon = SymbolRegular.DataHistogram24
},
Content = new DataViewModel()
}
};
NavigationFooter = new ObservableCollection<INavigationItemViewModel>
{
new NavigationItemViewModel()
{
Header = "Settings",
Tag = "settings",
Icon = new IconViewModel
{
Icon = SymbolRegular.Settings24
},
Content = new SettingsViewModel()
}
};
TrayMenuItems = new ObservableCollection<IMenuItemViewModel>
{
new MenuItemViewModel()
{
Header = "Home",
Tag = "tray_home"
}
};
Or something similar to this.
View side you should then bind to an items source, and provide an item template for the bound items, or an item template selector, to present the items. Something like
<ui:NavigationCompact
x:Name="RootNavigation"
Grid.Column="0"
Footer="{Binding ViewModel.NavigationFooter, Mode=OneWay}"
Frame="{Binding ElementName=RootFrame, Mode=OneWay}"
Items="{Binding ViewModel.NavigationItems, Mode=OneWay}">
<ui:NavigationCompact.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:NavigationItemViewModel}">
<ui:NavigationItem
Content="{Binding Header}"
Icon="{Binding Icon.Icon}"
IconFilled="{Binding Icon.Filled}"
IconSize="18"
PageTag="{Binding Tag}"
PageContent="{Binding Content}"/>
</DataTemplate>
</ui:NavigationCompact.ItemTemplate>
</ui:NavigationCompact>
same with the menu items.
The actual page content, which is, at this point the viewmodel, can just be presented by the control using a content control where I can specify the template in my application resources.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemesDictionary Theme="Dark" />
<ui:ControlsDictionary />
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type viewModels:DashboardViewModel}">
<pages:DashboardPage />
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
or even within the item template of the navigation item itself if you need to specify a specific page content item template or item template selector.
INavigationService and IPageService have very view specific methods like GetFrame etc, which shouldnt really be used in the viewmodel layer either. (they're passed into the MainWindowViewModel in the template). A navigation service in the viewmodel layer should solely be able to navigate via the page tag.
Describe alternatives you've considered
No response
Additional context
Overall this would provide an MVVM-respecting approach to the navigation, and completely segregate the view layers and the viewmodel layers.
I personally do not believe that the current implementation, and subsequently the WPF UI approved templates in the VS plugin, are a good example of what to do with the MVVM design pattern.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the template's MainWindowViewModel, NavigationCompact and NavigationItem usage, along with INavigationService and IPageService. Compare their view-specific responsibilities with the proposed view-model representations and tag-based navigation. Done should be a decided, documented approach that separates view-model and view-layer dependencies without leaving navigation behavior undefined.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- desktop, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100