Multiple views of the same data

Jul 22, 2008 at 8:40 PM
I have created the following items all contained in module Customer:

CustomerModel

ICustomerListPresenter/CustomerListPresenter  
ICustomerListView/CustomerListView (Defined as a listbox)

ICustomerDetailPresenter/CustomerDetailPresenter
ICustomerDetailView/CustomerDetailView

ICustomerCompositePresenter/CustomerCompositePresenter
ICustomerCompositeView/CustomerCompositeView

These are self contained in there own module with no external references.  When I load this in my main application everything is fine my composite view can come up with a customer list I double click on a fish and it brings up the details.

Now I want to add a Orders module.  Assuming the orders module is set up exactly like the above but has a static reference to the Customer module.  When I edit an order in details view, I want to have a combobox from which I can select my customer.  Do I need to make a new CustomerComboListView to fill in here or is there a way to use CustomerListView?

Feel free to tell me I am going in a completely wrong direction on this, but also please tell me a better one if you feel this is the case.  I didn't know if this was more of a wpf thing or a composite WPF thing.
Jul 25, 2008 at 7:59 PM

Hi

 

I recommend you to avoid having static references between modules. You can instead split in a separated project the common interfaces that your modules share. Then you can use the container to get the specific implementation.

 

Please, let me know if it helps.

 

Mariano Converti

http://blogs.southworks.net/mconverti

Jul 28, 2008 at 4:57 PM

Thanks for the reply Mariano.

I would agree that holding a static reference is not the best approach and defeats the purpose of loosely coupled, but I'm not sure how I would resolve it.  If I want a listbox and a combobox implementation of the list view the only code I really want to write is create a new implementation of CustomerListView.  The interfaces don't need to change or anything.  But I would have to register the two concrete list views with ICustomerListView and I don't know how to resolve this without using the concrete classes.  I'm not sure if I am being clear.  So please let me know if there is more clarification needed.

Jul 30, 2008 at 5:15 AM
I created a demo with full source code HERE that implements the solution as Mariano suggested; I created a common interface (ICustomerService) in a module shared by ModuleA and ModuleB and have both modules use the container to get the implementation (via a dependency attribute).

If response to the following:  

[CodeHulk] The interfaces don't need to change or anything.  But I would have to register the two concrete list views with ICustomerListView and I don't know how to resolve this without using the concrete classes.

Jul 30, 2008 at 3:55 PM
Edited Jul 30, 2008 at 4:21 PM
Thanks for the response Bill.  It served very well to show the dependancy injection on the properties, however it didn't really solve my problem.  There are two seperate views for the same data agreed, but the two views are created completely independant of each other.

One of the benefits of using interfaces is utilizing the same code with multiple different concrete classes without having to change the plumbing that uses them.  So looking at the EventAggregation example. 
In ModuleA (unmodified) we have the IAddFundView which contains no references to any controls or anything specific to UI design in general.  It simply states the interface that can be used when working with anything that derives from it.  The benefit in traditional OOP to doing this is I can now implement a StandardAddFundView, a WizardAddFundView, and a  AdvancedAddFundView all deriving from this interface(Standard, Wizard, and Advanced are just different UI representations of the same thing).  In fact, using our UI patters (mvp, presentationmodel, etc) we can use the same presenters when working with these different views as well. 

I have two options
Option 1:
IStandardAddFundView
StandardAddFundView : IStandardAddFundView
StandardAddFundPresenter

IWizardAddFundView
WizardAddFundView : IWizardAddFundView
WizardAddFundPresenter

IAdvancedAddFundView
AdvancedAddFundView : IAdvancedAddFundView
AdvancedAddFundPresenter

Option 2:

IAddFundView
AddFundPresenter
StandardAddFundView : IAddFundView
WizardAddFundView : IAddFundView
AdvancedAddFundView : IAddFundView

Obviously Option 2 holds leaves us in a better state for maintainability as we don't have as many classes to maintain and since the only difference in the views is the UI.  More or less what I am asking is how would one go about using option 2.  It would be possible to change the UI using Data Templates but wouldn't I then have to have either static references to every data template I want to use or find a way to inject them?

I hope this clears up what I am asking.  I'm not trying to argue either way I am just trying to learn some of the best practices of the best practices so to speak and just asking how other people would resolve this problem.  I'm sorry if I can't get my point across.  Its kind of hard to do in plain text.

Thanks again for the responses.
Jul 31, 2008 at 3:45 AM

Hi CodeHulk, option 2 seems to be pretty clear - here is my stab at it.

I have a XAML form that has two buttons and a label.  The buttons will both call the same event handler btnClick() and use the buttons name to resolve the service to use.

HelloWorldView.xaml file:

    <UserControl x:Class="HelloWorld.Views.HelloWorldView"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:HelloWorld"
       xmlns:cal="http://www.codeplex.com/CompositeWPF">
         
       <StackPanel>
          <TextBlock x:Name="lblResults" HorizontalAlignment="Center">HelloWorld</TextBlock>
           <Button Click="btnClick" x:Name="btnOne">Load HelloWorld1</Button>
           <Button Click="btnClick" x:Name="btnTwo">Load HelloWorld2</Button>
           <ContentControl
                  cal:RegionManager.RegionName="{x:Static local:RegionNames.HelloWorld}"/>
       </StackPanel>
    </UserControl>

In the btnClick() of the code behind you'll find that I have a service object (of type IHelloWorldService) that I need to resolve - it will use the Button.Name as indicated above.  The step after being resolved will use the applicable services GetMessage() to update the label, which in our case will contain the Hello World #1 or #2 message as applicable.  You can get more details, as well as the source code, HERE.

HelloWorldView.xaml.cs

    private void btnClick(object sender, System.Windows.RoutedEventArgs e)
    {
        Button button = sender as Button;
        IHelloWorldService service = _container.Resolve<IHelloWorldService>(button.Name);
        lblResults.Text = service.GetMessage();
    }

HelloWorldModule

    protected void RegisterViewsAndServices()
    {
        container
            .RegisterType<IHelloWorldService, HelloWorldService1>("btnOne")
            .RegisterType<IHelloWorldService, HelloWorldService2>("btnTwo");
       
    }

     public class HelloWorldService1 : IHelloWorldService
    {
        public string GetMessage()
        {
            return "********> Hello World #1 <********";
        }
    }

    public class HelloWorldService2 : IHelloWorldService
    {
        public string GetMessage()
        {
            return "======> Hello World #2 <======";
        }
    }

Am I getting closer to understanding your requirement?

 

Jul 31, 2008 at 2:05 PM

That's exactly what I am looking for thanks a lot Bill.  Sorry if this was an easy problem.  The more I thought about my problem the more I realized its not so much of a Composite WPF hang up as it is a unity one.  I just downloaded the unity documentation.  One last question though.  Using your above example, I have a class 

public class HelloWorld
{
    public HelloWorld(IHelloWorldService service)
    {
        MyService service;
    }

    public IHelloWorldService MyService {get;set;}
}

and then resolve as follows:

container.Resolve<HelloWorld>();

is there a way to specify which IHelloWorldService you want to be injected into the constructor of HelloWorld or would you manually resolve the service and use the MyService property to give HelloWorld a handle to it.


Thanks Much,
~Justin

Jul 31, 2008 at 3:45 PM

Hi Justin,

I haven't seen anything in the Unity documentation to suggest that there is named mapping support for Setter/Constructor injection.  Here is one way you approached it (in concept, I would use MVP in the real world).

Using the example provided I want HelloWorldService1 to resolve - I modified the btnClick() as follows:

        private void btnClick(object sender, System.Windows.RoutedEventArgs e)
        {
            //Button button = sender as Button;
            //IHelloWorldService service = _container.Resolve<IHelloWorldService>(button.Name);
            //lblResults.Text = service.GetMessage();

            HelloWorldController controller = _container.Resolve<HelloWorldController>();
            lblResults.Text = controller.GetMessage();

        }

This is what my controller looks like:

    public class HelloWorldController
    {
        private IHelloWorldService _service;

        public HelloWorldController(IUnityContainer container)
        {
            _service = container.Resolve<IHelloWorldService>("btnOne");
        }
        public string GetMessage()
        {
            return _service.GetMessage();
        }
    }

 

Jul 31, 2008 at 9:56 PM
That works for me. :)  Thanks a lot for your help Bill.

Though less dynamic I was able to get what I was looking for by making the presenter templated so I can pass in the the view during registration or resolution. 
Aug 22, 2008 at 7:55 AM
I believe there is named support as of Unity 1.1. You can use the Configuration API (Container.Configure) or you can specify through configuration.