firstfloorsoftware / mui

Modern UI for WPF
Microsoft Public License
2.6k stars 755 forks source link

MefContentLoader not resolving Constructor parameter #170

Open EddLonzanida opened 7 years ago

EddLonzanida commented 7 years ago

Hi,

I'm trying to create a simple IoC/DI scenario following this guide.

I'm getting this error:

System.Windows.Markup.XamlParseException: 'No matching constructor found on type 'TestMef.Pages.Settings.Appearance'. You can use the Arguments or FactoryMethod directives to construct this type.' Line number '19' and line position '6'. ---> System.MissingMethodException: No default constructor found for type 'TestMef.Pages.Settings.Appearance'. You can use the Arguments or FactoryMethod directives to construct this type.
   at System.Xaml.Schema.XamlTypeInvoker.DefaultCtorXamlActivator.EnsureConstructorDelegate(XamlTypeInvoker type)
   at System.Xaml.Schema.XamlTypeInvoker.DefaultCtorXamlActivator.CreateInstance(XamlTypeInvoker type)
   at System.Xaml.Schema.XamlTypeInvoker.CreateInstance(Object[] arguments)
   at MS.Internal.Xaml.Runtime.ClrObjectRuntime.CreateInstanceWithCtor(XamlType xamlType, Object[] args)
   at MS.Internal.Xaml.Runtime.ClrObjectRuntime.CreateInstance(XamlType xamlType, Object[] args)
   --- End of inner exception stack trace ---
   at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
   at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
   at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
   at System.Windows.Application.LoadBamlStreamWithSyncInfo(Stream stream, ParserContext pc)
   at System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean bSkipJournaledProperties)
   at System.Windows.Application.LoadComponent(Uri resourceLocator)
   at FirstFloor.ModernUI.Windows.DefaultContentLoader.LoadContent(Uri uri)
   at FirstFloor.ModernUI.Windows.DefaultContentLoader.<>c__DisplayClass0_0.<LoadContentAsync>b__0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()

Page with constructor parameter (DI via MEF):

    [ContentExport("/Pages/Settings/Appearance")]
    public partial class Appearance : UserControl, IContent
    {
        [ImportingConstructor]
        public Appearance(IAppearanceViewModel vm)  //<= Why the cause of Error? While other parameterless pages are working just fine.
        {
            InitializeComponent();
            DataContext = vm;
        }
        public void OnFragmentNavigation(FragmentNavigationEventArgs e){ }
        public void OnNavigatedFrom(NavigationEventArgs e){ }
        public void OnNavigatedTo(NavigationEventArgs e){ }
        public void OnNavigatingFrom(NavigatingCancelEventArgs e){}
    }

    //ViewModel decalaration (in a separate file)
    [Export(typeof(IAppearanceViewModel))]
    public class AppearanceViewModel
        : NotifyPropertyChanged, IAppearanceViewModel
    {
            //skipped contents for brevity
    }

App.xaml:

<Application x:Class="TestMef.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
                <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
                <ResourceDictionary Source="/Resources/AppResources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

AppResources.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:mui="http://firstfloorsoftware.com/ModernUI"
                xmlns:testMef="clr-namespace:TestMef">
    <Style x:Key="ModernFrame" TargetType="mui:ModernFrame">
        <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
    </Style>
    <Style x:Key="ModernTab" TargetType="mui:ModernTab">
        <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
    </Style>
    <Style x:Key="MainWindow" TargetType="testMef:MainWindow">
        <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
    </Style>
</ResourceDictionary>

MefContentLoader:

    [Export]
    public class MefContentLoader : DefaultContentLoader
    {
        private readonly IEnumerable<Lazy<IContent, IContentMetadata>> contents;
        [ImportingConstructor]
        public MefContentLoader([ImportMany]IEnumerable<Lazy<IContent, IContentMetadata>> contents)
        {
            this.contents = contents;
        }

        protected override object LoadContent(Uri uri)
        {
            // lookup the content based on the content uri in the content metadata
            var content = contents.Where(r => r.Metadata.ContentUri == uri.OriginalString)
                .Select(r=>r.Value)
                .FirstOrDefault();
            if (content == null) throw new ArgumentException("Invalid uri: " + uri);
            return content;
        }
    }

Please advise.

Thank you in advance.

Ghislain1 commented 6 years ago

MefContentLoader must get a default contructor. find another away to pass the contents of MefContentLoader