rid00z / FreshMvvm

FreshMvvm is a super light Mvvm Framework designed specifically for Xamarin.Forms. It's designed to be Easy, Simple and Flexible.
Apache License 2.0
597 stars 173 forks source link

Resolve Pages from different assemblies than the PageModel #218

Open robinmanuelthiel opened 6 years ago

robinmanuelthiel commented 6 years ago

Currently, the FreshPageModelResolver resolves Pages only from the same assembly as the PageModel. That implies that when having PageModels and Pages in different projects for example, resolution of the Page fails.

// Fails when MainPage lives in a different assembly than MainPageModel
var page = FreshPageModelResolver.ResolvePageModel<MainPageModel>();

Why would I want that?

Personally, I like to have PageModels and Pages in different projects. I share PageModels across all frontend technologies (Xamarin.Forms for iOS and Android, Xamarin.Mac and UWP without Xamarin.Forms). Although all these projects might structure their pages differently that can all use the same PageModel. So I have a MyProject.Frontend.Sharedproject for my PageModels and reference that form MyProject.Frontend.Forms.iOS, MyProject.Frontend.UWP, ... and so on.

How to solve that today

Fortunately, FreshMVVM is very easy to extend so that I simply wrote my own PageModelMapper class that takes an alternative assembly and namespace as parameters.

public class MyFreshPageModelMapper : IFreshPageModelMapper
{
    readonly string pageNamespace;
    readonly string pageAssemblyName;

    public MyFreshPageModelMapper(string pageNamespace = null, string pageAssemblyName = null)
    {
        this.pageNamespace = pageNamespace;
        this.pageAssemblyName = pageAssemblyName;
    }

    public string GetPageTypeName(Type pageModelType)
    {
        var assemblyQualifiedName = pageModelType.AssemblyQualifiedName;

        // Replace Namespace
        if (pageNamespace != null)
            assemblyQualifiedName = assemblyQualifiedName.Replace(pageModelType.Namespace, pageNamespace);

        // Replace Assembly
        if (pageAssemblyName != null)
            assemblyQualifiedName = assemblyQualifiedName.Replace(pageModelType.Assembly.ToString(), pageAssemblyName);

        // Replace "Model"
        assemblyQualifiedName = assemblyQualifiedName
            .Replace("PageModel", "Page")
            .Replace("ViewModel", "Page");

        return assemblyQualifiedName;
    }
}

With that, I can easily call the following before I resolve any Page with FreshMvvm.

// Configure FreshPageModelResolver to resolve Pages 
// from different Namespaces/Assemblies than the ViewModel
FreshPageModelResolver.PageModelMapper =
    new MyFreshPageModelMapper(
        typeof(MainPage).Namespace,
        typeof(MainPage).Assembly.ToString());

What I propose

I would love to see the default PageModelMapper getting extended by the configuration I have shown above to become more flexible when resolving Pages.

I am more than happy to submit a Pull Request for this but first wanted to hear what you guys think!

Hammurabidoug commented 6 years ago

This is a great idea. What are the alternatives if you want your pages and pagemodels in separate projects?

robinmanuelthiel commented 6 years ago

@Hammurabidoug The current workaround is what I described above.

Hammurabidoug commented 6 years ago

@robinmanuelthiel Yeah. I tried it and it didn't work. We'll try to look again at some point.

robinmanuelthiel commented 6 years ago

Works for me! I used it here, if you want to take a look: https://github.com/MikeCodesDotNet/Mobile-Cloud-Workshop/blob/master/Mobile/ContosoFieldService.Core/Helpers/PageModelLocator.cs

mpalmer78 commented 5 years ago

@robinmanuelthiel nice addition! I too had struggled with resolving my "Views" and "ViewModels" but built my own IFreshPageModelMapper so that I could keep my Views in the Views folder/namespace and my ViewModels in my ViewModels folder/namespace. Call me crazy... but isn't MVVM supposed to be about Models, Views, and ViewModels? It's not called MPPM, so I'm confused where this lingo came from and why it persists. Regardless, I'm really liking FreshMvvm so far, so I'm willing to ignore that it violates my own personal version of naming logic. Thanks for sharing your code.

robinmanuelthiel commented 5 years ago

@mpalmer78 You are correct, the whole "PageModel" thing annoys me as well. But luckily, you can also call your classes "ViewModels" like MyCoolViewModel and FreshMVVM will find them as well, as described here: https://github.com/rid00z/FreshMvvm#conventions

michael-palmer-apc commented 5 years ago

Yes, I read that section on the conventions but in practice I did not find it to be the case. FreshMvvm was not able to resolve my ViewModels or Views that were contained within respective ViewModels/Views namespaces. I had to implement my own IFreshPageModelMapper in order to properly resolve my types. Regardless, I'm glad the ability to build my own mapper is available in FreshMvvm.

robinmanuelthiel commented 5 years ago

If the ViewModel and View are in the same assembly, MyTestPage should resolve MyTestViewModel. And MyTestView should also resolve MyTestViewModel.

Laumania commented 5 years ago

I couldn't make the View/ViewModel convention, which is the correct one if you ask me and the OCD in me cannot live with this Page/PageModel thing - so totally agree with @mpalmer78

I solved it simply by creating this little PageModelMapper:

public class MvvmConventionPageModelMapper : IFreshPageModelMapper
    {
        public string GetPageTypeName(Type pageModelType)
        {
            return pageModelType.AssemblyQualifiedName.Replace("ViewModel", "View");
        }
    }

And then you just set it in the App.xaml.cs as part of your IoC setup, something like this:

public App()
        {
            InitializeComponent();
            InitializeDependencies();

            var homeView        = FreshPageModelResolver.ResolvePageModel<HomeViewModel>();
            var mainNavigation  = new FreshNavigationContainer(homeView);

            MainPage            = mainNavigation;
        }

        private void InitializeDependencies()
        {
            FreshPageModelResolver.PageModelMapper = new MvvmConventionPageModelMapper();

            FreshIOC.Container.Register<IYouTubeService, YoutubeService>();

        }
EmilAlipiev commented 4 years ago

that is seriously needs to be implemented this will help to work with Tizen also. Tizen implements a CirclePage which is inherited from ContentPage but since it uses different namespace it is impossible to use freshmvvm. thats why this is very important.

rid00z commented 4 years ago

Hey

We can already do this, I will write up some content but basically there’s a interface that you can implement to control the resolution.

On Sat, 31 Aug 2019 at 12:41 pm, Emil Alipiev notifications@github.com wrote:

that is seriously needs to be implemented this will help to work with Tizen also. Tizen implements a CirclePage which is inherited from ContentPage but since it uses different namespace it is impossible to use freshmvvm. thats why this is very important.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rid00z/FreshMvvm/issues/218?email_source=notifications&email_token=AALZPPIILCSCFHYSZ2LQGOTQHHK6LA5CNFSM4ESH5VIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5TDTVA#issuecomment-526793172, or mute the thread https://github.com/notifications/unsubscribe-auth/AALZPPN5JFKKHJOBZLBV65LQHHK6LANCNFSM4ESH5VIA .

--

[image: XAM-Consulting Pty Ltd] http://www.xam-consulting.com/

[image: Twitter] https://twitter.com/XAMConsulting [image: LinkedIn] https://www.linkedin.com/company-beta/5791295/

Michael Ridland / Director / Xamarin & Microsoft MVP michael@xam-consulting.com / 0404 865 350

XAM Consulting Pty. Limited. Web | Cloud | Apps | Xamarin 210/29 Kiora Rd, Miranda NSW 2228 xam-consulting.com http://www.xam-consulting.com/

EmilAlipiev commented 4 years ago

Hi @rid00z

would you mind sharing more information or if you posted somewhere else? thanks a lot

EmilAlipiev commented 4 years ago

@robinmanuelthiel is your code not slow? are you doing this once when your application is loaded or everytime you navigate to a viewmodel?