October 2010 - Posts
As part of the task where I had to upgrade an existing WPF project to PRISM 4.0 and work with MEF, one of the requirements was to support registering existing non-attributed parts.
Consider the following example -
Code Snippet
- public interface IFoo { }
-
- public class Foo : IFoo { }
-
- static void Main(string[] args)
- {
- //This is the desired thing to do
- myCatalog.Register<IFoo, Foo>();
- }
As you can see, type Foo has no export definitions set on it.
One approach would be to go over all the registrations and add attributes where needed, or create wrappers for legacy components, but the goal in our case was to support this kind of registration.
After digging around, I found two ways that you can go about it -
- Implement a conventional catalog using the ReflectionModelServices which is the MEF’s API for building reflection related parts information.
- Example - CodePaste.NET - A conventional catalog for MEF (Prototype)
- Use the MEF’s AttributedModelServices with a TypeDelegator to expose the actual type as an attributed type even though it doesn’t have attributes defined directly on it.
This is quite a sophisticated solution and I decided to stick with that one. - Example - Poco, Mef, and custom type systems. Are you ready to take the red pill
You can look at the links to see the actual code of each solution. Plus, you can integrate it with the type catalog which supports type registration in runtime which I described in my previous post.
Recently, I dug deep into MEF because I was involved in a project where there was a decision to upgrade the WPF client to PRISM 4 and advance to using MEF alone instead of Unity.
While many argue that MEF isn’t yet a full DI framework, it still provides an easy way to enable application extensibility in your projects.
One of the tasks that I had while upgrading the client to PRISM 4, is to keep supporting type registration in runtime, something in the sort of the following -
Code Snippet
- public interface IFoo
- {
- }
-
- [Export(typeof(IFoo))]
- public class Foo : IFoo
- {
- }
-
- catalog.Register<Bar>();
The built-in TypeCatalog that comes with MEF requires you to specify the types in the constructor and there is no support for registrations in runtime for the same catalog.
In order to support this feature, I could simply add a new TypeCatalog for every registration request in runtime with the desired type, but that seemed to be kind of wasteful.
MEF does support catalog changes in runtime, you need to implement ‘INotifyComposablePartCatalogChanged’.
The solution
I implemented a type catalog that supports registrations in runtime, here is the core implementation -
Code Snippet
- public void Register(IEnumerable<Type> types)
- {
- if (types != null)
- {
- List<ComposablePartDefinition> addedParts = new List<ComposablePartDefinition>();
-
- foreach (Type type in types)
- {
- ComposablePartDefinition part = AttributedModelServices.CreatePartDefinition(type, null);
-
- addedParts.Add(part);
- }
-
- using (var atomicComposition = new AtomicComposition())
- {
- OnChanging(new ComposablePartCatalogChangeEventArgs(addedParts, EmptyPartDefinitions, atomicComposition));
-
- _lock.EnterWriteLock();
- try
- {
- foreach (var addedPart in addedParts)
- {
- _parts.Add(addedPart);
- }
- }
- finally
- {
- _lock.ExitWriteLock();
- }
-
- atomicComposition.Complete();
- }
-
- OnChanged(new ComposablePartCatalogChangeEventArgs(addedParts, EmptyPartDefinitions, null));
- }
- }
If I need to register known types in runtime, I can just do the following -
Code Snippet
- RuntimeTypeCatalog catalog = new RuntimeTypeCatalog();
-
- CompositionContainer container = new CompositionContainer(catalog);
-
- catalog.Register<Bar>();
- catalog.Register<Foo>();
Feel free to download the full source code and see it in action.
If you have a DateTime and you would like to get the localized name of the day according to the current culture, you can just do the following -
myDateTime.ToString(“ddd”);
I had a different scenario though, I had an object with a property of DayOfWeek enum which represents week days.
I needed to display just that property in my UI as a localized string.
In this case, I don’t have a DateTime so I can’t use its culture-based string formatting, so I can’t use the approach written above.
I could obviously generate a DateTime that is of such a day and use the that approach , but that seemed quite wasteful.
Well, fear not, there is a way to do that -
You can get the DateTimeFormatInfo in your current culture (be that the CurrentCulture or the CurrentUICulture), and retrieve the day name straight from there.
Code Snippet
DayOfWeek day = DayOfWeek.Monday;
System.Globalization.CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
string dayOfWeekLocalized = cultureInfo.DateTimeFormat.DayNames[(int)day];
Update 26/09/2011 - There is an issue with the control where the entire tree is loaded, which may affect performance and memory allocation when working with many items. Unfortunately, I don’t think I would be able to fix that any time soon.
A few years back, I built a WPF control that I decided to share here now.
The control is called ComboView – A ComboBox showing a TreeView inside to display hierarchical data (you can use it for flat data if you need its features though).
The control provides some really nice built-in features that you can use.
The control uses another control that I had to write, called ‘ExtendedTreeView’. This control inherits from TreeView and adds some features on top of that.
Another thing, I used the ‘Reveal’ control provided by Bag’o’Tricks, it has some very cool stuff, worth a look.
Following is a common mode that you can use it for -
In order to see all the options and features, feel free to download the code with the showcases here – Zuker.WpfSamples.zip
Known Limitations:
- If you wish to bind to the SelectedViewItems or SelectedViewValues (Supports Two-Way), the source property must be of type ‘ObservableCollection<object>’
- If you wish to use SelectedViewValue / SelectedViewValues, you should set SyncSelectedViewValues to true.
- If you set selection in code – items or values, the SelectedViewItems property of the ComboView will not necessarily include these items right away.
- This is because of the true nature of WPF’s TreeView, I may have to expand the items internally
- If you’re dependent on that in the next line, put this logic in Dispatcher.BeginInvoke, it should be ready there.
- Since there were some patches along the way, and features that were added at a later stage, the code can be organized better
One of the core components in WCF Contrib is the ‘ClientChannel’ which handles the client side proxy creation and management, allowing you to easily call services, with the addition of environment support, using using, and comprehensive behaviors extensions model.
Currently, the built-in Client Channels can be instantiated with a configuration name which it loads the ChannelFactory with.
A common question is, how can you use the Client Channel if you like to instantiate the ChannelFactory in code.
The way to do that is to inherit from ClientChannel and override ‘CreateChannelFactoryCore’ and just return the ChannelFactory of your wish.
A more general solution, one that I recommend in every company that wants to use WCF Contrib, is to write your own base Client Channel classes to be used everywhere in your code.
This approach gives you a singular point where you can control how every Client Channel behaves and plug-in default behavior extensions.
Following is an example of such base classes. This example allows the developer to pass in a ChannelFactory to be used, that way, we enable the scenario of using a Channel Factory through code.
Code Snippet
public class MyClientChannel<T> : MyClientChannel<MyClientChannel<T>, T>
where T : class
{
public MyClientChannel() { }
public MyClientChannel(ChannelManageOptions manageOptions)
: base(manageOptions) { }
public MyClientChannel(ChannelFactory channelFactory)
: base(channelFactory) { }
public MyClientChannel(ChannelFactory channelFactory, ChannelManageOptions manageOptions)
: base(channelFactory, manageOptions) { }
}
public class MyClientChannel<TInstance, T> : ClientChannel<TInstance, T>
where T : class
where TInstance : ClientChannel<T>, new()
{
private ChannelFactory _factory;
public MyClientChannel()
{
OnInitialize(null);
}
public MyClientChannel(ChannelManageOptions manageOptions)
: base(manageOptions)
{
OnInitialize(null);
}
public MyClientChannel(ChannelFactory channelFactory)
{
OnInitialize(channelFactory);
}
public MyClientChannel(ChannelFactory channelFactory, ChannelManageOptions manageOptions)
: base(manageOptions)
{
OnInitialize(channelFactory);
}
void OnInitialize(ChannelFactory channelFactory)
{
//You can decide to disable factory caching if you provide it with a factory which is cached already
//CacheChannelFactory = (channelFactory != null);
_factory = channelFactory;
}
protected override ChannelFactory CreateChannelFactoryCore(string endpointName)
{
if (_factory == null)
{
//If the factory wasn't provided from outside, just call the base which tries to load it from the configuration
_factory = base.CreateChannelFactoryCore(endpointName);
}
return _factory;
}
}
Then, you can use it as follows -
Code Snippet
ChannelFactory<IService> myFactory = new ChannelFactory<IService>(
new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:11000/MyService"));
using (MyClientChannel<IService> myClientChannel = new MyClientChannel<IService>(myFactory))
{
myClientChannel.Channel.Do(null);
}