Search code examples
mvvmuwpprismwindows-10-universalprism-6

Prism Auto ViewModelLocator with Subdirectories in UWP


I don't know if it is a good pratice, but I would like to use subdirectories into the directories "Views" and "ViewModels"

I have this code right now

namespace Project.Universal.ViewModels.Sales
{
    public class SalesListViewModel:ViewModelBase
    {
        // Properties
        private string _test;
        private ICollection<Sale> _sales;

        public string Test
        {
            get => _test;
            set => SetProperty(ref _test, value);
        }

        public ICollection<Sale> Sales {
            get => _sales;
            private set
            {
            }
        }

        // Services
        private readonly IStandardService<Sale> _salesService;

        // Commands
        public DelegateCommand Update { get;}

        // Constructor
        public SalesListViewModel(IStandardService<Sale> salesService)
        {


            Debug.WriteLine(">>>> Initializing SalesListViewModel, trying to set _salesService.");
            _salesService = salesService;

            Debug.WriteLine(">>>> Delegating command to update SalesListViewModel.");
            Update = new DelegateCommand(async () =>
            {
                Sales = await _salesService.GetAsync();
                Test = "Test";
            });
        }

    }
}

And this is my view page

<mvvm:SessionStateAwarePage
    x:Class="Project.Universal.Views.Sales.SalesListPage"
    mvvm:ViewModelLocator.AutoWireViewModel="True"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Project.Universal.Views.Sales"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:mvvm="using:Prism.Windows.Mvvm"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Margin="{StaticResource MediumLeftTopRightBottomMargin}">
        <TextBlock>this is the Sales page</TextBlock>
        <TextBlock Text="{Binding Test}"></TextBlock>
    </Grid>
</mvvm:SessionStateAwarePage>

But Prism do not inject the view model correctly. Unline this, the Prism Framework is injecting ShellViewModel, like error bellow says:

Error: BindingExpression path error: 'Test' property not found on 'Project.Universal.ViewModels.ShellViewModel'. BindingExpression: Path='Test' DataItem='Project.Universal.ViewModels.ShellViewModel'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')

For organization, I want to use this pattern for folders:

Views/Context/ViewPage.xaml for views
ViewModels/Context/ViewModel.cs for viewModels

I'm using a template from Windows Template Studio with Prism 6.3 framework. Sorry for my english.


Solution

  • After hours I've resolute my problem:

    The Windows Template Studio have changed the ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver() method to mantain compatibility to others frameworks. I've just changed this method like I want and that worked. For others that will have same problem, here's the code.

    Class App.xaml.cs

        protected override async Task OnInitializeAsync(IActivatedEventArgs args)
            {
                await ThemeSelectorService.InitializeAsync().ConfigureAwait(false);
    
                ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
                {
                    var viewName = viewType.FullName; // get full type name
                    var viewModelName = viewName
                        .Substring(0, viewName.Length - 4)
                        .Replace(".Views.", ".ViewModels.");
                    var viewModelAssembly = viewType.GetTypeInfo().Assembly.FullName;
                    //var viewModel
                    var viewModelTypeName = string.Format(CultureInfo.InvariantCulture, "{0}ViewModel, Project.Universal", viewModelName);
    
                    return Type.GetType(viewModelTypeName);
                });
                await base.OnInitializeAsync(args);
            }