Caliburn-Micro / Caliburn.Micro

A small, yet powerful framework, designed for building applications across all XAML platforms. Its strong support for MV* patterns will enable you to build your solution quickly, without the need to sacrifice code quality or testability.
http://caliburnmicro.com/
MIT License
2.8k stars 778 forks source link

Views not found when AssemblySourceCache is installed? #791

Open robbiehinch opened 2 years ago

robbiehinch commented 2 years ago

I'm upgrading an application from v2 to v4 Caliburn.Micro. The Bootstrapper was registering view assemblies like this:

            AssemblySourceCache.Install();
            var viewAssemblies = GetViewAssemblies();
            AssemblySource.Instance.AddRange(viewAssemblies);

Having upgraded to v4, the views are not found. This can be understood when I look at the implementation of AssemblySourceCache.Install and AssemblySourceCache.ExtractTypes.

Source code link

ExtractTypes filters the types down to those assignable from INotifyPropertyChanged e.g. ViewModels and not Views, and they are what is stored on the TypeNameCache in the Install routine. Additionally AssemblySource.FindTypeByNames is updated to only return the types in the TypeNameCache. So AssemblySource.FindTypeByNames no longer finds the Views. I can avoid the problem by not calling AssemblySourceCache.Install(), but I understood from this document of the v3 release notes that unless types implement INotifyPropertyChanged or subclass from UIElement then they won't be found. But the implementation doesn't seem to agree with that statement (it only honours the INotifyPropertyChanged implementation and not UIElement). Have I missed some extra info that explains how the types subclassed from UIElement should be installed to the Cache? In order to workaround that I understand from the same v3 release notes I could reimplement AssemblySourceCache.ExtractTypes to make this work, but it seems I have not found the proper way to do it and I'm hoping somebody can point me to that, please?

KasperSK commented 2 years ago

Looks like it was changed from 3.2 including UIElement to 4.0 only including INotifyPropertyChanged. I am not sure why this change was made.

As for making it work as you intent. You could do this somewhere in your bootstrapper:

AssemblySourceCache.ExtractTypes = assembly => assembly.GetExportedTypes() .Where(t => typeof(UIElement).IsAssignableFrom(t) || typeof(INotifyPropertyChanged).IsAssignableFrom(t));

I will see if I can figure out why it was changed to only include INotifyPropertyChanged

Edit: Actually thinking about it it must have been because AssemblySourceCache was moved to the platforms core part of the code which is shared between platforms and thus it would be agnostic of UIElement.

KasperSK commented 2 years ago

Which dotnet version are you using?

robbiehinch commented 2 years ago

Hi Kasper .NET Framework 4.8

sirosimo commented 2 years ago

Hi,

Any update on this issue ? I’m facing a similar issue where I can’t seem to find a way to register Views that are injected from external assemblies. I have several User Controls (external DLLs, using a different namespace than the main application) that are loaded by a dependency injector (Castle Windsor) and I’m making sure that the views and viewmodels are being registered from these DLL. But when I inject a view model, I can’t seem to be able to find the corresponding View. I tracked it to the same root in the AssemblySourceCache.Install, where the TypeNameCache doesn’t have any reference of the Views injected. Would a sample project help to move this issue forward ?

KasperSK commented 2 years ago

Hi @sirosimo,

A sample of the issue would be great, if you can make one I will gladly look into why this happens.

/Kasper

sirosimo commented 2 years ago

Thank you for checking Kasper. I think it is mostly due to my skills levels :) I was able to bind a ViewModel to a View (after resolving through DI Factories) but I forgot to activate the ViewModel after that. I think I've something that half works, I'm able to see my views at one point but then I'm not able to display it back after switching between different viewmodel.

My issue may not be related to the issue robbiehinch raised but I posted an example project here. I opened a discussion #818 to see if someone could shine on it and provide some friendly guidance . Fingers crossed.

Thank you for your help

KasperSK commented 2 years ago

Hi again,

Have you tried overwriting SelectAssemblies in the bootstrapper and the returning a list of assemblies which contain views?

/Kasper

KasperSK commented 2 years ago

Hi @robbiehinch,

Like in the above comment I would love to hear if you are also overwriting SelectAssemblies?

/Kasper