Note – this is a multi part post:
Today I had the opportunity to take a look at the code written in the previous posts and insert some new stuff in order to modify the project and make new experiments about:
-
using a different ViewModel class for the design-time and run-time;
-
-
retrieve the data using an async call to a WCF service and passing back the results to the VM via MEF.
1 – Using different VM classes
To accomplish this task I’ve defined a new interface named IMainPageViewModel defining these members:
/// MainPage ViewModel interface
public interface IMainPageViewModel : IViewModelBase
{
string aViewModelProperty { get; set; }
DataItems dataItems { get; set; }
ICommand addDataItemCommand { get; }
}
This new interface is then implemented by two classes named ViewModels.DesignMode.MainPageViewModel and ViewModels.MainPageViewModel:
/// ViewModel for the "MainPageView" used in design-mode
public class MainPageViewModel : ViewModelBase, IMainPageViewModel
{
public MainPageViewModel()
{
//Initialize the properties with test data if design mode
aViewModelProperty = "Value - Design Mode";
//Initialize the "dataItems" property
dataItems = new DataItems();
dataItems.Add(new DataItem() { Description = "Sample Data Item 1 - Design Mode" });
dataItems.Add(new DataItem() { Description = "Sample Data Item 2 - Design Mode" });
}
public string aViewModelProperty { get; set; }
public DataItems dataItems { get; set; }
public ICommand addDataItemCommand { get; set; }
}
/// ViewModel class for the "MainPageView" using MEF
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(MainPageViewModel))]
public class MainPageViewModel : ViewModelBase, IMainPageViewModel
{
public MainPageViewModel() { }
[Import("aViewModelPropertyTextProvider")]
public string aViewModelProperty { get; set; }
[Import(typeof(WcfDataItems))]
public DataItems dataItems { get; set; }
[Import(typeof(ICommand))]
public PartCreator<ICommand> addDataItemCommandCreator { get; set; }
private ICommand _addDataItemCommand;
public ICommand addDataItemCommand
{
get {
if (_addDataItemCommand==null)
_addDataItemCommand = addDataItemCommandCreator.CreatePart().ExportedValue;
return _addDataItemCommand;
}
}
}
The first one is associated with the View at design-time using an attached property which permits to bind an instance only at design time using this xaml (usually you should use <d:DesignProperties.DataContext>, here we are experimenting, of course):
<UserControl x:Class="SL4_MVVM_MEF.Views.MainPageView"
........
xmlns:designer="clr-namespace:SL4_MVVM_MEF.Designer"
xmlns:providersDM="clr-namespace:SL4_MVVM_MEF.Providers.DesignMode"
>
<!-- Design time DataContext -->
<designer:Page.DesignDataContext>
<providersDM:MainPageViewModelProvider/>
</designer:Page.DesignDataContext>
........
DesignTimeDataContext attached dependency property:
/// DesignDataContext Attached Dependency Property
public static readonly DependencyProperty DesignDataContextProperty =
DependencyProperty.RegisterAttached("DesignDataContext", typeof(object), typeof(Page),
new PropertyMetadata((object)null,
new PropertyChangedCallback(OnDesignDataContextChanged)));
/// Gets the DesignDataContext property.
public static object GetDesignDataContext(DependencyObject d)
{
return (object)d.GetValue(DesignDataContextProperty);
}
/// Sets the DesignDataContext property.
public static void SetDesignDataContext(DependencyObject d, object value)
{
d.SetValue(DesignDataContextProperty, value);
}
/// Handles the DesignDataContext property changes
private static void OnDesignDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
//Get the ViewModel instance only in design mode
if ((Application.Current == null) || (Application.Current.GetType() == typeof(Application)))
element.DataContext = e.NewValue;
}
And this is the design-time mode in Blend using the new attached property:

2 – MEF and implicit styles
The new implict styles feature available in Silverlight 4 beta is used to initialize the DataContext of the MainPageView type in a declarative way in App.xaml:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/ xaml/presentation"
xmlns:x= "http://schemas.microsoft.com/winfx/2006/xaml"
x:Class= "SL4_MVVM_MEF.App"
xmlns:views= "clr-namespace:SL4_MVVM_MEF.Views"
xmlns:providers= "clr-namespace:SL4_MVVM_MEF.Providers">
<Application.Resources>
<!-- Run-time DataContext composed using MEF -->
<Style TargetType="views:MainPageView">
<Setter Property="DataContext">
<Setter.Value>
<providers:MainPageViewModelMEFProvider/>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
In this case I’ve modified the MainPageViewModelMEFProvider class and inserted a new IViewModelProvider interface in order to obtain the instance of the ViewModel initialized by MEF:
/// Interface for the ViewModelProvider
public interface IViewModelProvider
{
object GetViewModel { get; }
}
/// Get the ViewModel instance using MEF
public class MainPageViewModelMEFProvider : IViewModelProvider
{
public MainPageViewModelMEFProvider() { }
[Import(typeof(MainPageViewModel))]
public IViewModelBase ViewModel { get; set; }
/// Get the Instance of the ViewModel using MEF
public object GetViewModel
{
get
{
PartInitializer.SatisfyImports(this);
return ViewModel;
}
}
}
3 – retrieve the data using an async call to a WCF service
I’ve added to the solution a simple WCF service which returns a collection of DataItems:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class DataItemService : IDataItemService
{
[OperationContract]
public List<DataItemFromService> GetDataItems()
{
// Add your operation implementation here
return new List<DataItemFromService>()
{
new DataItemFromService() {Description="DataItem from service 1"},
new DataItemFromService() {Description="DataItem from service 2"},
new DataItemFromService() {Description="DataItem from service 3"}
};
}
}
interface IDataItemService
{
List<DataItemFromService> GetDataItems();
}
public class DataItemFromService
{
public string Description { get; set; }
}
Since the project uses MEF composition and [Import] /[Export] attributes to initialize all the members of the VM class, I’ve used the same approach for the dataItems collection, retrieving data from an async call to the WCF service using a WcfDataItems class:
/// A sample collection of DataItems from WCF
[Export(typeof(WcfDataItems))]
public class WcfDataItems : DataItems
{
public WcfDataItems()
{
//Initialize the collection
DataItemWcfService.DataItemServiceClient svc = new DataItemWcfService.DataItemServiceClient();
svc.GetDataItemsCompleted += (s1, e1) =>
{
if (e1.Result != null)
e1.Result.ToList().ForEach(d =>
{
//Retrieve a new DataItem
DataItem di = DataItemCreator.CreatePart().ExportedValue;
di.Description = d.Description;
this.Add(di);
});
isLoading = false;
};
svc.GetDataItemsAsync();
isLoading = true;
}
[Import(typeof(DataItem))]
public PartCreator<DataItem> DataItemCreator { get; set; }
}
The code is available for download here.
Happy Silverlighting!