{"record":{"id":"44a0a570119fbdc4","repo":"PrismLibrary/Prism","slug":"you-have-referenced-a-view-type-and-are-likely-breaking-the","errorCode":null,"errorMessage":"You have referenced a View type and are likely breaking the MVVM pattern. You should never reference a View type from a ViewModel.","messagePattern":"You have referenced a View type and are likely breaking the MVVM pattern\\. You should never reference a View type from a ViewModel\\.","errorType":"exception","errorClass":"NavigationException","httpStatus":null,"severity":"error","filePath":"src/Maui/Prism.Maui/Navigation/Builder/NavigationBuilderExtensions.cs","lineNumber":21,"sourceCode":"using Prism.Navigation.Builder;\n\nnamespace Prism.Navigation;\n\npublic static class NavigationBuilderExtensions\n{\n    /// <summary>\n    /// Creates a <see cref=\"INavigationBuilder\"/> using the current instance of the <see cref=\"INavigationService\"/>.\n    /// </summary>\n    /// <param name=\"navigationService\">The <see cref=\"INavigationService\"/>.</param>\n    /// <returns><see cref=\"INavigationBuilder\"/></returns>\n    public static INavigationBuilder CreateBuilder(this INavigationService navigationService) =>\n           new NavigationBuilder(navigationService);\n\n    internal static string GetNavigationKey<TViewModel>(object builder)\n    {\n        var vmType = typeof(TViewModel);\n        if (vmType.IsAssignableFrom(typeof(VisualElement)))\n            throw new NavigationException(NavigationException.MvvmPatternBreak, typeof(TViewModel).Name);\n\n        if (builder is not IRegistryAware registryAware)\n            throw new Exception(\"The builder does not implement IRegistryAware\");\n\n        return registryAware.Registry.GetViewModelNavigationKey(vmType);\n    }\n\n    public static INavigationBuilder RelativeBack(this INavigationBuilder builder) =>\n        builder.AddSegment(\"..\");\n\n    /// <summary>\n    /// This will force the generated Navigation URI to return an Absolute URI resetting the current <see cref=\"Window\"/>'s <see cref=\"Page\"/> property.\n    /// </summary>\n    /// <param name=\"builder\">The <see cref=\"INavigationBuilder\"/>.</param>\n    /// <returns>The <see cref=\"INavigationBuilder\"/>.</returns>\n    public static INavigationBuilder UseAbsoluteNavigation(this INavigationBuilder builder) =>\n        builder.UseAbsoluteNavigation(true);\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/PrismLibrary/Prism/blob/358118cd640d9a22ff8cf21c8ad197fa038b7990/src/Maui/Prism.Maui/Navigation/Builder/NavigationBuilderExtensions.cs#L3-L39","documentation":"GetNavigationKey<TViewModel> guards the MVVM pattern: if TViewModel is actually assignable from VisualElement (i.e. a Page/View type was passed where a ViewModel is expected), it throws NavigationException with code MvvmPatternBreak. Prism's builder API resolves navigation keys from ViewModel registrations, so passing a View defeats the design.","triggerScenarios":"Calling .CreateBuilder<TView>(...) style extension like NavigateAsync<MainPage>() where MainPage : ContentPage, i.e. a View type used as the generic argument instead of its ViewModel.","commonSituations":"Choosing the page class from IntelliSense autocomplete instead of the ViewModel; projects without a 1:1 ViewModel naming convention; generics where typeof(T) ended up being a VisualElement-derived type.","solutions":["Pass the ViewModel type instead: NavigateAsync<MainPageViewModel>() and register the ViewModel-to-View mapping via ViewModelLocator or ViewRegistration.","Ensure the mapping exists: either [Register]","If a View type is genuinely required, use the string-key based navigation (NavigateAsync(\"MainPage\")) rather than the ViewModel-typed builder."],"exampleFix":"// before\nnavigationService.CreateBuilder<MainPage>() // View type -> MVVM break\n    .NavigateAsync();\n// after\nnavigationService.CreateBuilder<MainPageViewModel>()\n    .NavigateAsync();","handlingStrategy":"type-guard","validationCode":"if (typeof(T).IsAssignableFrom(typeof(VisualElement)) || typeof(VisualElement).IsAssignableFrom(typeof(T)))\n    throw new ArgumentException($\"{typeof(T).Name} is a View; pass the ViewModel type instead.\");","typeGuard":"bool IsViewModel<T>() => !typeof(VisualElement).IsAssignableFrom(typeof(T));","tryCatchPattern":"try\n{\n    await navigationService.CreateBuilder<TViewModel>().NavigateAsync();\n}\ncatch (NavigationException ex) when (ex.Code == NavigationException.MvvmPatternBreak)\n{\n    logger.LogError(ex, \"A View type was passed where a ViewModel was expected\");\n}","preventionTips":["Adopt a strict *ViewModel naming convention and only ever pass those types to the builder.","Register every ViewModel-to-View mapping so ViewModel keys resolve.","Enable analyzer/code-review checks that flag VisualElement-derived generics in navigation calls."],"tags":["mvvm","navigation","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"358118cd640d9a22ff8cf21c8ad197fa038b7990","analyzedAt":"2026-09-15T15:00:31.079Z","contentChangedAt":"2026-09-15T15:00:31.079Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}