Deeper integration SI with WPF

Oct 29, 2014 at 7:50 PM
Edited Oct 29, 2014 at 10:04 PM
Hi there,

I've found solution to get a container instance directly from XAML.
Actually there are 2 solutions:
  1. simple one, when you do not keep the Window or UserControl in the container
  2. I keep everything in the container (the container composes the project from several independent assemblies), so it's impossible to directly get an instance in XAM - it means register and get instance at once. So I found a deferred binding solution and adopted it to work with container.
    Is it interesting or not?
As for me - it greatly simplifies the development. No RelativeSource any more :-)

Regards,
Mikhail
Coordinator
Oct 29, 2014 at 8:23 PM
Hi @msin, we are always keen to see the different ways people make use of the our container, please show us what you did! :-)
Coordinator
Oct 29, 2014 at 8:55 PM
Is this related to this stackoverflow question?
Oct 29, 2014 at 8:57 PM
Edited Oct 29, 2014 at 9:40 PM
Well, 1st step is very simple - preparing the container Markup Extension:
using System;
using System.Windows.Markup;

namespace Lipro.Base
{
//
// Markup extension that resolves an instance from the IoC container.
// Based on Mike Hillberg's Blog on Wpf and Silverlight
// http://blogs.msdn.com/b/mikehillberg/archive/2006/10/06/limitedgenericssupportinxaml.aspx
//

   public class ContainerExtension : MarkupExtension
   {
       public Type Type { get; set; }
   
       // ProvideValue, which returns an object instance from the container
       public override object ProvideValue(IServiceProvider serviceProvider)
       {
           //  Use the weakly typed version to get the instance
           return IoC.Instance.GetInstance(Type);
       }
   }
}
Now it's possible to set DataContext in XAML for any FrameworkElement in this way:
<UserControl ... DataContext="{base:Container Type=interafaces:IViewModel}" ...>
Or make the direct binding to container instance property:
<TextBox Text="{Binding Name, Source={base:Container Type=interfaces:IViewModel}}" />
But last line works when IViewModel registered as a singleton.
So you set DataContext or bind to property of ViewModel instance in XAML and may now forget these nice binding samples:
<TextBlock Name="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Name}">
<TextBlock Name="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}, AncestorLevel=2}, Path=Name}">
Oct 29, 2014 at 8:59 PM
Edited Oct 29, 2014 at 9:31 PM
@dot_NET_Junkie - No, I didn't read this question
Oct 29, 2014 at 9:19 PM
Edited Oct 29, 2014 at 10:20 PM
But I have to make next step, because I used to register a MainWindow in the container and it doesn't work - during registering the window tries to get DataContext from the container.
So I had to find a way to defer the container reply. There are many lines of code but principal method is next:
    //
    //  Based on Ifeanyi Echeruo's blog WPF Recipe - Deferred Markup Extension 
    //  http://blogs.msdn.com/b/ifeanyie/archive/2010/03/27/9986217.aspx
    //

    if (TryGetFromProvideValueTarget(provideValueTarget, out setter))
        Task.Factory.StartNew(() => SetDataContext(serviceProvider, setter));

 private void SetDataContext(IServiceProvider serviceProvider, IProvideValueTargetSetter setter)
 { 
     //  Wait until the container is verified and all instances are registered 
     while (!IoC.Instance.IsVerified) 
         Thread.Sleep(50);                                                                                                                                                              

  //  Set Value of DependencyProperty of DependencyObject (mostly it is FrameworkElement.DataContext) 
     setter.SetValue(IoC.Instance.GetInstance(Type)); 
     setter.Dispose(); 
}  
In this way the window is registered with DataContext=null, but there is a Task, which keeps the DependencyObject instance (MainWindow), the DependencyProperty instance (DataContext) and waits for the container verification.
After the verification of the container the task sets the DataContext value and stops.

Full code of the ContainerExtention.cs is here:
https://yadi.sk/i/QMhXtUeacNiZE
Oct 29, 2014 at 9:29 PM
Edited Oct 29, 2014 at 9:36 PM
@dot_NET_Junkie - I didn't know about lazy registration before. Thanks
Coordinator
Oct 29, 2014 at 10:49 PM
@msin,

Have you considered putting this onto github with a working example with unit tests (see here)?
Oct 29, 2014 at 11:02 PM
Edited Oct 29, 2014 at 11:09 PM
qujck wrote:
Have you considered putting this onto github with a working example with unit tests (see here)?
Yes, I'm going to do that.
I use Bitbucket for my projects, but I've got GitHub account.

By the way, I have no idea what to test... especially in case of a simple markup extension.
Call to the container is made from UI thread, it will be difficult make separated unit tests.
But I will try :-)
Coordinator
Oct 29, 2014 at 11:15 PM
Good man :-)

bitbucket is fine, I have an account there too - if you put together a simple working example we can probably figure something out between us.
Oct 30, 2014 at 1:24 AM
Edited Oct 30, 2014 at 1:22 PM
Added new repo with demo application:
https://github.com/msin/SimpleInjectorExtensionApp

And more complex demo application
https://github.com/msin/SiComplexDemoApp

The last one you can start from LIB folder, but to compile you need a valid DevExpress subscription or trial version of WPF components:
https://go.devexpress.com/DevExpressDownload_UniversalTrial.aspx

No unit tests for a while...