In CodeHulk's message thread Multiple Views of the Same Data mconverti responded with the following answer:
"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."
CodeHulk's response to this was that "I don't know how to resolve this without using the concrete classes". Since I was preparing to open up the EventAggregation solution to learn how it handles event processing, and it happened to have two modules, this served as a good environment to address CodeHulks question using mconverti's suggestion.
Module A is the view on the left and Module B is the view on the right. I have a Listbox in Module A and a ComboBox in Module B being serviced by the same interface and service.
Since Module A is instantiated before Module B we'll register dependencies in it. Once registered we can simply resolve it in any other module that requires it; in this example we're using a [Dependency] atttribute in the a Module B presenter.
Module B code file updates follow - updates are relative to MyComboBox:
IActivityView.cs
public interface IActivityView
{
ComboBox MyComboBox { get; set; }
void AddContent(string content);
string Title { set; }
string CustomerId { set; }
}
ActivityView.xaml.cs
Public ComboBox MyComboBox
{
get { return MyComboBoxControl; }
set { MyComboBoxControl = value; }
}
ActivityView.xaml
<StackPanel Name="ContentPanel">
<Label Name="ActivityLabel" FontWeight="Bold" Padding="0" Margin="0"/>
<ComboBox
HorizontalAlignment="Left"
x:Name="MyComboBoxControl"
ItemsSource="{Binding}" DisplayMemberPath="Name"
Width="100"
/>
</StackPanel>
ActivityPresenter.cs
// Replaced View with the following code
// public IActivityView View { get; set; }
private IActivityView _view;
public IActivityView View
{
get { return _view; }
set {
_view = value;
OnViewSet();
}
}
private ICustomerService _customerService;
[Dependency]
public ICustomerService CustomerService
{
set { _customerService = value; }
}
public void OnViewSet()
{
_view.MyComboBox.DataContext =
_customerService.GetCustomerList();
}
Now let's look at how Module A was configured to make the above possible:
First we'll register our ICustomerService interface, it will resolve to the LocalCustomerService class - the only changes to existing code were the addition of the AddServices() method:
public class ModuleA : IModule
{
public ModuleA(IUnityContainer container, IRegionManager regionManager)
{
Container = container;
RegionManager = regionManager;
AddServices(container);
}
private void AddServices(IUnityContainer container)
{
container.RegisterType<ICustomerService, LocalCustomerService>();
}
With that done you'll find that Module A's code closely resembles the code updates made to Module B
IAddFundView.cs
public interface IAddFundView
{
ListBox MyListBox { get; set; }
event EventHandler AddFund;
string Customer { get;}
string Fund { get;}
}
AddFundView.xaml.cs
public ListBox MyListBox
{
get { return MyListBoxControl; }
set { MyListBoxControl = value; }
}
AddFundView.xaml
<Button Name="AddButton" Margin="5" Width="75" Height="25" Style="{DynamicResource SimpleButton}" HorizontalAlignment="Left">Add</Button>
<ListBox
x:Name="MyListBoxControl"
ItemsSource="{Binding}" DisplayMemberPath="Name"
Width="100" Height="100"></ListBox>
</StackPanel>
AddFundPresenter.cs
public IAddFundView View
{
get { return _view; }
set
{
_view = value;
_view.AddFund += AddFund;
OnViewSet();
}
}
private ICustomerService _customerService;
[Dependency]
public ICustomerService CustomerService
{
set { _customerService = value; }
}
public void OnViewSet()
{
_view.MyListBox.DataContext =
_customerService.GetCustomerList();
}
Since the EventAggregation.Infrastructure project was being referenced by both ModuleA and ModuleB I placed the following classes in the projects new Services folder:
ICustomerService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventAggregation.Infrastructure.Services
{
public interface ICustomerService
{
List<CustomerInfo> GetCustomerList();
}
}
----
CustomerInfo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventAggregation.Infrastructure.Services
{
public class CustomerInfo
{
public int ID { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public CustomerInfo(int id, string name, string phone)
{
ID = id;
Name = name;
Phone = phone;
}
}
}
----
LocalCustomerService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventAggregation.Infrastructure.Services
{
public class LocalCustomerService : ICustomerService
{
public List<CustomerInfo> GetCustomerList()
{
List<CustomerInfo> returnList = new List<CustomerInfo>();
returnList.Add(new CustomerInfo(0, "Bill", "555-1212"));
returnList.Add(new CustomerInfo(1, "Diane", "555-1200"));
return returnList;
}
}
}
Note: If you are going to add this code to your EventAggregation solution be sure to update your ModuleA and ModuleB .Tests projects to include the following references:
PresentationCore
PresentationFramework
WindowsBase
You can then implement the interfaces as applicable
Side note: In this blog entry I was focused on the statement "I don't know how to resolve this without using the concrete classes". From reading the documentation, more specifically the Technical Concepts | Region section, it suggest that a View can be reused - the following is an exceprt:
To add a view to a region, get the region from the region manager, and call the Add method, as shown in the following code.
IRegion region = _regionManager.Regions["MainRegion"];
var ordersPresentationModel = _container.Resolve<IOrdersPresentationModel>();
var _ordersView = ordersPresentationModel.View;
region.Add(_ordersView, "OrdersView");
region.Activate
Tags:
compositewpf,
unity
Categories:
CompositeWPF |
Unity |
WPF