Refactoring ContentControl driven Views to MEFedMVVM

Oct 22, 2010 at 10:53 PM
Edited Oct 22, 2010 at 11:07 PM

Now that I think I want to use MEFedMVVM generally, I want to make sure my application that I started can be MEFactorized...

 

I have a MainViewWindow/MainViewModel with that code:

XAML of MainWindow.cs:

<Window.Resources>
        <ViewModel:MainViewModel x:Key="MainViewModelID" />
        <DataTemplate DataType="{x:Type ViewModel:WeeklyViewModel}">            <View:WeeklyView/>        </DataTemplate>      
        <DataTemplate DataType="{x:Type ViewModel:DailyViewModel}">            <View:DailyView/>        </DataTemplate>
        <DataTemplate DataType="{x:Type ViewModel:MonthlyViewModel}">            <View:MonthlyView/>        </DataTemplate>
</Window.Resources>
...
<ContentControl Content="{Binding VM}" />
...

Alle 3 ViewModels inherit from ViewModelBase and the View that is shown in the ContentControl is loaded via buttons commands when the user presses one of the 3 buttons (daily view, weekly view, monthly view) 
just like this: 

...
        public MainViewModel()
        {            
            dailyVM= new DailyViewModel();            
            VM = dailyVM;           
        }       

        public ViewModelBase VM 
        {
            get { return _vm; } 
            set
            {
                if (_vm != value)
                {
                    _vm = value;
                    this.RaisePropertyChanged("VM");
                }
            }
        } 
The switching via buttons a sample: 

  public RelayCommand MonthlyViewCommand
        {
            get { return _monthlyViewCommand ?? (_monthlyViewCommand = new RelayCommand(() => VM = new MonthlyViewodel() )); }
        } 

I could make the MainViewModel working with MEFedMVVM for this I have the sample demo, but how can I make the ContentTemplate switching with MEFedMVVM because I have to do
an action when the user presses a button. MEFedMVVM is doing everything automatically. I need Control... the switching. 
My question is about this link: http://marlongrech.wordpress.com/category/mefedmvvm/

What if the DataContext is set because I am in a DataTemplate? How can I get Services injected and some design time data as well?

Will the ViewModel be created everytime? 
Marlon:" This UserControl will still have a ViewModel set as its DataContext but it must not be set from MEFedMVVM ViewModelLocator.ViewModel attached property"
When my I can annotate my 3 ViewModels with an ExportAttribute should it not be possible to add the ViewModel objects to a "list" and everrytime the same button is pressed again load the same ViewModel from the
"list" so I keep the state? Now I am a bit confused ;-)
It would be great if anyone here could share his wisdom and experience how to cope with that scenario in a good way.
Oct 23, 2010 at 12:22 AM

I am not sure I understand it 100% but this may be along the similar line:

http://mefedmvvm.codeplex.com/Thread/View.aspx?ThreadId=225709&ProjectName=MEFedMVVM

 

Oct 23, 2010 at 7:41 AM

just similar ;-) yes I want to set the true parameter to make it DataContextAware. What I want too is to save the state of the Daily/Weekly/MontlyViewMOdel and for me it seems I have to save those ViewMOdels which get exchanged in the ContentControl

in a separate List<ViewModelBase> and every time I press a button to change the view I have to access the List<ViewModelBase> either via index or the Name of the ViewModel like "Daily View" etc... The whole List should actually remain saved because the MainViewModel

is handled fully by MefedMVVM and the ViewModelLocator. That means its created ONE time and reused. So the List is reused too.

Can someone tell me please am I in the right direction?

Coordinator
Oct 23, 2010 at 12:13 PM

Not sure I am following but I think all you need is just manage the instances of the ViewModels u r creating i.e

BEFORE

 public RelayCommand MonthlyViewCommand
        {
            get { return _monthlyViewCommand ?? (_monthlyViewCommand = new RelayCommand(() => VM = new MonthlyViewodel() )); }
        } 

AFTER

MonthlyViewodel _monthlyVM = new MonthlyViewodel();
 public RelayCommand MonthlyViewCommand
        {
            get { return _monthlyViewCommand ?? (_monthlyViewCommand = new RelayCommand(() => VM = _monthlyVM  )); }
        } 
Oct 23, 2010 at 5:34 PM

yes Marlon that I tried before and it makes sense of course to keep the viewmodel as class variable so everytime the month-button is pressed I get the former month object and set it to VM = ...

but that did not work many months ago when I started with MVVM. Anyway I just put together a sample demo with MEFedMVVM + MVVM Light where I will try that contentcontrol stuff (again) and

the new MEFedMVVM love. I will let you know prolly tomorrow evening and upload a demo here :-)

Oct 23, 2010 at 11:11 PM
Edited Oct 23, 2010 at 11:46 PM

Since I imported the IMessenger object from MVVM light toolkit in my Ctor the exporting of the ViewModel does not work anymore??

I can just paste here the german error and translate:

An composition error occured. No valid exports were found etc... What do I wrong?

 

When I used the IMediator it worked that means no output window error/warning and my binding worked.

 

System.ComponentModel.Composition Warning: 1 : Die ComposablePartDefinition "TBM2011.ViewModel.NavigationViewModel" wurde zurückgewiesen. Die Komposition bleibt unverändert. Die Änderungen wurden aufgrund der folgenden Fehler zurückgewiesen: Bei der Komposition ist ein Kompositionsfehler aufgetreten. Die Ursache wird unten angegeben. Die CompositionException.Errors-Eigenschaft liefert genauere Informationen.

1) Es wurden keine gültigen Exporte gefunden, die der Einschränkung "((exportDefinition.ContractName == "GalaSoft.MvvmLight.Messaging.IMessenger") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "GalaSoft.MvvmLight.Messaging.IMessenger".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))" entsprechen.

Ungültige Exporte wurden möglicherweise abgelehnt.
Ergebnis: Import "TBM2011.ViewModel.NavigationViewModel..ctor (Parameter="messenger", ContractName="GalaSoft.MvvmLight.Messaging.IMessenger")" kann nicht für Teil "TBM2011.ViewModel.NavigationViewModel" festgelegt werden.Element: TBM2011.ViewModel.NavigationViewModel..ctor (Parameter="messenger", ContractName="GalaSoft.MvvmLight.Messaging.IMessenger") -->  TBM2011.ViewModel.NavigationViewModel -->  DirectoryCatalog (Path="C:\TBM2011\TBM2011\bin\Debug\")

 

UPDATE:

I have uploaded a sample here but the switching of the Views in the ContentControl does not yet work, because something is wrong with the DataContextAwareness... Marlon do you know? If that filehoster is not ok for you, I can upload where you want :-)

http://www.sendspace.com/file/d23t86  120 kb .7z file with VS2010 project.

Oct 24, 2010 at 1:18 AM

Anything that gets MEF-Imported  must be declared as MEF-exportable (using MEF Export attribute) somewhere. You can't add some class to the system and expect that MEF knows how to instantiate it without some setup. In this example you would probably need to change MVVM Light and add Export attribute  on IMessenger interface. You would also need to add light toolkit to the MEF assembly catalog so that it knows about it. This has nothing to do with MEFedMVVM by itself but is rather a basic MEF.

Oct 24, 2010 at 10:42 AM
Edited Oct 24, 2010 at 12:38 PM

yes Tonko prestep1 should do the trick: http://devlicio.us/blogs/derik_whittaker/archive/2010/03/06/providing-metadata-to-you-mef-exports.aspx

and about my project I do not know why the buttons has access to the datacontext , but the ContentControl is dead. I debugged binding etc doesnt matter I will try MEFedMVVM on my real project where the contentcontrol works

and the export thingie, thanks for help cu later....

 

ok this is really cool Marlon. In my real project everrything works:

- Visual state is saved (seems only for controls with TwoWay binding but that is natural ;-)),

- ViewModel is created ONE time,

- Got rid of double property changes where I did not know where they come from...

- Can import own classes when annotated with [Export(typeof(IMessenger))]    public class Messenger : IMessenger    {...}

that rocks :P