MEF and WPF controls with composed imports

Jun 15, 2009 at 7:59 PM

Are there any patterns/examples on how to integrate MEF with WPF where WPF controls that are specified in XAML can also have properties that are MEF imports.  Does this take a XAML extension?

Jun 15, 2009 at 8:31 PM
Edited Jun 15, 2009 at 8:33 PM

There are several ways to approach this problem. Currently we don't have a great solution in the box, but there are some community samples. We are currently working on adding MEF/XAML integration support which will ship out-of-band for WPF, and hopefully sooner on my blog (I have been meaning ot post on this for a while). 

If you want to drop parts into the VisualTree that have imports:

1. The easiest way to handle it is to name those elements (x:Name) which will create a field in the code behind. Then you can call Container.SatisfyImportsOnce in the code behind passing in each element that needs to be composed.

2. You can also create an attached property like Compose that will make the call for you. Then you can use that attached property on each element that has imports i.e. <Button m:Mef:Compose=True/> and you don't have too set the x:Name.

3. A different approach is to have a markup extension that allows you to to declaratively wire up dependeny properties to specific contracts. This allows you to compose things that are natively non-MEF aware.

In the work we are doing, we are allowing parts to be composed, without requiring anything to be specified in the xaml that contains them.

As far as samples, there is a blog post out there that illustrates approach 3, though I am not able to find it currently Here is a link to an early version of our prototype that you are free to pull from http://cid-f8b2fd72406fb218.skydrive.live.com/self.aspx/blog/WPF%20Composition.zip which covers 2 and 3.

HTH

Glenn

Jun 15, 2009 at 8:36 PM

Found the post I was looking for:

http://denisvuyka.wordpress.com/2009/02/23/bringing-mef-to-xaml-via-markupextension/

 

Jun 15, 2009 at 9:29 PM

Thanks Glenn

I am definently interested in approach 3 and between the article and your code I think I have a solution. 

Am I correct in my reading of your response, that approach 3 is something that you intended on supporting out of the box?

Also, do you have a relative time frame on the next MEF drop?  I realize extact dates are not possible but a simple "weeks", "months" or "many months" would be of some benefit.

Thanks

Jun 15, 2009 at 9:59 PM
As far as release, I am not sure yet, but I suspect it will be weeks. I've pinged our team to see about locking down on our next drop.
We're not planning to ship any specific Xaml support out of the box. We are looking to add support as part of a Silverlight release, and to shop the support OOB for WPF.

We are looking at supporting scenario 3, however our highest pri is to support parts that can be added to the element hierarchy without requring any specific xaml markup, which is I beleive what addresses the scenario you initially asked about , in other words...

<UserControl>
  <MyCustomButton/>
</UserControl>

If MyCustomButton had imports, they would automatically be satisfied.

Isn't that were you asking for initially?

 

 

 

Jun 15, 2009 at 10:15 PM

Glenn,

Re: Timeframe - thanks!

Re: Support - yes the scenerio of MyCustomButton having imports that would be automatically added to the element hierarchy without that is exactly what I would like to see.

Jul 2, 2009 at 5:54 PM
Edited Jul 2, 2009 at 6:06 PM

Hi

From the second post in this thread:
As far as samples, there is a blog post out there that illustrates approach 3, though I am not able to find it currently Here is a link to an early version of our prototype that you are free to pull from http://cid-f8b2fd72406fb218.skydrive.live.com/self.aspx/blog/WPF%20Composition.zip which covers 2 and 3.

I've been looking on this example. There is a reference to "assembly=System.ComponentModel.Composition.Windows"

I am using MEF preview 5 and I cant find the assembly there..

 

//lasse

Jul 2, 2009 at 6:30 PM

Hi Lassel

System.ComponentModel.Composition.Windows does not ship as part of MEF. The project is included within that zip file.

HTH

Glenn

Jul 2, 2009 at 6:33 PM

Sorry,, I should looked better :)

Thanks

//lasse

Jul 3, 2009 at 9:53 AM

Hi again

I've added System.ComponentModele.Composition.Windows to my solutions. It compile nicely but gives me a runtime error (that I not had before) that points to a style declaration in my xaml file. It says that he count find that style. This is the line thats gives the problem.

                                <Controls1:AddToListBoxControl Style="{DynamicResource AddToListBoxControl}" mef:Composition.Compose="True">
The error message is:
Cannot find the type of the property ''.  Error at object 'System.Windows.Controls.ControlTemplate' in markup file 'lgTronic.ActiveWork.Modules.PersonCollection;component/personcollectionview.xaml' Line 119 Position 81.
The line and position pointed at is the Style declaration. This error is intruduced by adding "mef:Composition.Compose="True" "
 
My AddToListBoxControl looks like:
    public class AddToListBoxControl : ListBox, IPartImportsSatisfiedNotification
    {
        //
        private ListView _listCnt;
        private ComboBox _prefixCnt;
        private TextBox _dataCnt;

        [Import]
        private IAddToListBoxPresenter presenter;

        public AddToListBoxControl()
        {
            this.DefaultStyleKey = typeof(AddToListBoxControl);
        }

        public void OnImportsSatisfied()
        {
           
        }
    }
The xaml file using this looks like (I've simplified it a bit):
 
            <Style x:Key="PersonCRUDTemplate" TargetType="{x:Type ContentControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContentControl}">
                            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="5"/>

                                </Grid.ColumnDefinitions>
                                <TextBlock Margin="1,2,3,1" Foreground="{DynamicResource HighFontColor}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Notes" TextWrapping="Wrap" Grid.Row="9"/>
                                <TextBlock Margin="1,2,3,1" Foreground="{DynamicResource HighFontColor}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Files" TextWrapping="Wrap" Grid.Row="10"/>

                                <TextBox Margin="1,2,1,1" Style="{DynamicResource TextBoxStyle}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding Path=Item.Birthday, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" TextWrapping="Wrap" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"/>
                                <Controls1:AddToListBoxControl Margin="1,2,1,1" Style="{DynamicResource AddToListBoxControl}" mef:Composition.Compose="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Path=Item.PersonalEmails, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" ItemTemplate="{StaticResource EmailColumnTemplate}" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2"/>

                            </Grid>

                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
The "missing" AddToListBoxControl looks like (also a bit simplyfied):
 
            <Style TargetType="{x:Type Controls1:AddToListBoxControl}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Controls1:AddToListBoxControl}">
                            <Border >
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
Any ideas about my problems?????  
//regards lasse 
Jul 3, 2009 at 6:12 PM

Did you add the mef namespace declaration at the top of the xaml?

 

Glenn

Jul 4, 2009 at 8:52 PM
Edited Jul 4, 2009 at 8:53 PM

Hi

I've have the following line

    xmlns:mef="clr-namespace:System.ComponentModel.Composition.Windows;assembly=System.ComponentModel.Composition.Windows"

 

//lasse

Jul 5, 2009 at 6:30 PM

One step forward......

The code above, is part of a module called by my main application. By adding a reference to "System.ComponentModel.Composition.Windows" to my main application the error described above disappeared. That good :)

Not so good is .. that the instanciation of the module stops to work. The following line:

var presenter = _container.GetExportedObject<IPersonCollectionPresenter>();

returns the following error...

{Cannot get export 'lgTronic.ActiveWork.Modules.PersonCollection.PersonPresenter (ContractName="lgTronic.ActiveWork.Modules.PersonCollection.IPersonCollectionPresenter")' from part 'lgTronic.ActiveWork.Modules.PersonCollection.PersonPresenter'.}

 

The only difference is I made, was adding the refernce above. If I remove it, the first error apperars again.

//lasse

 

 

 

Jul 6, 2009 at 11:52 AM

Ok, I think I now whats happend...

It seems that I did not understand the complet solution of your, which resultet in a mix-up between different composition containers.
In my solution I followed the examples given on Mef wiki pages and manually added parts to the container. In the given example parts are added using Xaml.

Is there a way to combine the two differtent approaches?

 

//lasse

Jul 6, 2009 at 6:08 PM

Hi Lasse

I see. Using the example parts can still be added manually as there is a container behind the scenes, but why are you adding parts manually to the container? In general catalogs should be used for providing parts to MEF. The one exception to that is during the bootstrapping, it is common to add something like the application class to the container in order to have it's imports satisfied.  In the spike, the application class automatically has it's imports satisfied, thus you can simply add imports on the app class.

Can you send a code snippet, or email me your sample project (gblock@microsoft.com)

Thanks

Glenn

Jul 7, 2009 at 7:52 AM

Hi

By adding manually,,,, I ment that I added assemlys to mef in code behind,, like the examples given. Your apporach using xaml looked more "automatic" to me :)

At the moment my application is working but I am not qute sure why :)

First of all, I done i automatic ;),,,, I have rewritten it so that assemblys are added in XAML. This mean that the following line:

var presenter = _container.GetExportedObject<IPersonCollectionPresenter>();

is changed to: (no strange whit this)

var provider = Application.Current.GetApplicationExportProvider();
var presenter = provider.GetExportedObject<IPersonCollectionPresenter>();

presenter is a PersonCollectionPresenter and looks like:

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IPersonCollectionPresenter))]
public class PersonPresenter : PresenterBase, IPersonCollectionPresenter
{

        [ImportingConstructor]
        public PersonPresenter(IPersonCollectionView _collection)
            : base(_collection)
        {

        }

}

IPersonCollectionView is a UserControl and contains the xaml code I've showed you before (contains PersonCrudTemplate etc). The c# code looks like ...

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IPersonCollectionView))]
public partial class PersonCollectionView : UserControl, IPersonCollectionView
    {

        public PersonCollectionView()
        {
            try
            {

                InitializeComponent();
            }
            catch(Exception ex)
            {
            }

 }

InitializeComponent indicates that there are some problems. Just doing like your example gives a Exception from this function. I've not been able to trace it, so I dont understand why. This exception only occures when
mef:Composition.Compose="True" is set for the AddToListBoxControl as show above.

If I adds mef:Composition.Compose="True" to the  PersonCollectionView the exeption disappers..... (just to be clear)

<UserControl x:Class="lgTronic.ActiveWork.Modules.PersonCollection.PersonCollectionView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Converters="clr-namespace:lgTronic.ActiveWork.Infrastructure.Converters;assembly=lgTronic.ActiveWork.Infrastructure" xmlns:Validation="clr-namespace:lgTronic.ActiveWork.Infrastructure.Validation;assembly=lgTronic.ActiveWork.Infrastructure"
    Loaded="UserControl_Loaded" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:Controls="clr-namespace:lgTronic.ActiveWork.Infrastructure.Controls;assembly=lgTronic.ActiveWork.Infrastructure" 
    xmlns:Controls1="clr-namespace:lgTronic.ActiveWork.Modules.PersonCollection.Controls" mc:Ignorable="d"
    xmlns:mef="clr-namespace:System.ComponentModel.Composition.Windows;assembly=System.ComponentModel.Composition.Windows"
    mef:Composition.Compose="True"
   >
    <UserControl.Resources>
        <ResourceDictionary>
...
...
..

So,,, the key for me was to add mef:Composition.Compose="True"  to the "parent control" of the custom control that orginally gave me problems..........................................

I really dont understand this,,, because according to example it should wok anyway regardless this line... 

Given time I will try to recreate my problems in the WPFComposition example....  because I am affraid that my code will break again if the problem not is clearly understod...

 

 

//regards
//lasse

 

 

 

Nov 11, 2010 at 6:32 PM

Hi Glenn,

Is there any follow up on this?  In my WPF app I'm declaring usercontrols in XAML and need their child imports to be resolved.  The containing parent usercontrol was resolved with MEF (via Prism4 module loader), but the children aren't.

Any suggestions other than the above? 

Thanks.

Mar 30, 2011 at 2:18 PM

I would also be interested in any follow up to this. Is there anything more?