Using MEF to Expose Interfaces in Your Silverlight MVVM Apps: Listing 3

Custom INavigationContentLoader

[Export] public class CompositionNavigationContentLoader : 
  INavigationContentLoader { 
  [ImportMany(typeof(IView))] 
  public IEnumerable<ExportFactory<IView, IViewMetadata>> 
    ViewExports { get; set; }

  [ImportMany(typeof(IViewModel))] 
  public IEnumerable<ExportFactory<IViewModel, IViewModelMetadata>> 
    ViewModelExports { get; set; }  

  public bool CanLoad(Uri targetUri, Uri currentUri) { 
    return true; 
  }  

  public void CancelLoad(IAsyncResult asyncResult) { 
    return; 
  }

  public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, 
    AsyncCallback userCallback, object asyncState) { 
    // Convert to a dummy relative Uri so we can access the host. 
    var relativeUri = new Uri("http://" + targetUri.OriginalString, 
      UriKind.Absolute);  

    // Get the factory for the ViewModel. 
    var viewModelMapping = ViewModelExports.FirstOrDefault(o => 
      o.Metadata.Key.Equals(relativeUri.Host, 
      StringComparison.OrdinalIgnoreCase)); 

    if (viewModelMapping == null) 
      throw new InvalidOperationException( 
        String.Format("Unable to navigate to: {0}. " +
          "Could not locate the ViewModel.", 
          targetUri.OriginalString));  

    // Get the factory for the View. 
    var viewMapping = ViewExports.FirstOrDefault(o => 
      o.Metadata.ViewModelContract == 
      viewModelMapping.Metadata.ViewModelContract); 

    if (viewMapping == null) 
      throw new InvalidOperationException( 
        String.Format("Unable to navigate to: {0}. " +
          "Could not locate the View.", 
          targetUri.OriginalString));  

    // Resolve both the View and the ViewModel. 
    var viewFactory = viewMapping.CreateExport(); 
    var view = viewFactory.Value as Control; 
    var viewModelFactory = viewModelMapping.CreateExport(); 
    var viewModel = viewModelFactory.Value as IViewModel;  

    // Attach ViewModel to View. 
    view.DataContext = viewModel; 
    viewModel.OnLoaded();  

    // Get navigation values. 
    var values = viewModelMapping.Metadata.GetArgumentValues(targetUri); 
    viewModel.OnNavigated(values);  

    if (view is Page) { 
      Page page = view as Page; 
      page.Title = viewModel.GetTitle(); 
    } 
    else if (view is ChildWindow) { 
      ChildWindow window = view as ChildWindow; 
      window.Title = viewModel.GetTitle(); 
    }  

    // Do not navigate if it's a ChildWindow. 
    if (view is ChildWindow) { 
      ProcessChildWindow(view as ChildWindow, viewModel); 
      return null; 
    } 
    else { 
      // Navigate because it's a Control. 
      var result = new CompositionNavigationAsyncResult(asyncState, view); 
      userCallback(result); 
      return result; 
    } 
  }  

  private void ProcessChildWindow(ChildWindow window, 
    IViewModel viewModel) { 
    // Close the ChildWindow if the ViewModel requests it. 
    var closableViewModel = viewModel as IClosableViewModel; 

    if (closableViewModel != null)  { 
      closableViewModel.CloseView += (s, e) => { window.Close(); }; 
    }  

    // Show the window. 
    window.Show(); 
  }  

  public LoadResult EndLoad(IAsyncResult asyncResult) { 
    return new LoadResult((asyncResult as 
      CompositionNavigationAsyncResult).Result); 
  }
}