2007/01/08
January 1st, 2007, I started working for Microsoft and will be moving to http://blogs.msdn.com/martijnh/. Hope to see you there!
2006/12/26
Outlook: Recovering e-mail messages from attachments
Recently, I was what I thought to be dragging e-mail from my Inbox into my Personal Folders. Outlook warned me that the process could take some time due to the amount of messages selected, but I went ahead with the move. After a while, Outlook popped up a freshly created e-mail message containing my 1000+ e-mail messages!
The new e-mail was about 85MB and the original message were gone from my inbox... I really wanted those e-mails in my Personal Folders. However, Outlook programming is something I've always shied away from. After some experimenting, I discovered what I wanted couldn't be easily done using Outlook APIs...
I wanted to:
This code misses all kinds of nice sanity checks, like whether the attachment actually is a MSG file, etc. It sufficed for my scenario and I now have the e-mails back. FYI :-D
The new e-mail was about 85MB and the original message were gone from my inbox... I really wanted those e-mails in my Personal Folders. However, Outlook programming is something I've always shied away from. After some experimenting, I discovered what I wanted couldn't be easily done using Outlook APIs...
I wanted to:
- Open the e-mail message containing the attached 1000+ 'backups'
- Iterate the attachments
- Save the attachments as e-mails into my Personal Folders box
// To use this namespace you must set a
// reference to the Microsoft Outlook 11.0
// COM server.
using Microsoft.Office.Interop.Outlook;
using OutLookApp = Microsoft.Office.Interop.Outlook.Application;
using System.IO;
using rd = Redemption;
ApplicationClass outLookApp = new ApplicationClass();
NameSpace outlookNS = outlookApp.GetNamespace("MAPI");
MailItem attachmentsItem = (MailItem)outlookNS.GetItemFromID(messageId, null);
// left out for clarity: find the Personal Folders->Inbox folder
rd.MAPIUtils utils = new rd.MAPIUtils();
utils.MAPIOBJECT = outlookNS.MAPIOBJECT;
foreach (Attachment a in attachmentsItem.Attachments)
{
string attachmentFile = Path.GetTempFileName();
a.SaveAsFile(attachmentFile);
MailItem underlyingItem =
(MailItem)personalFolder.Items.Add(OlItemType.olMailItem);
rd.MessageItem neww =
utils.GetItemFromMsgFile(attachmentFile, false);
underlyingItem.Save();
MailItem placedItem =
(MailItem)underlyingItem.Move(personalFolder);
neww.CopyTo(placedItem);
}
This code misses all kinds of nice sanity checks, like whether the attachment actually is a MSG file, etc. It sufficed for my scenario and I now have the e-mails back. FYI :-D
2006/12/24
WPF - themes in SDK
I'm probably the last one to notice, but just in case I'm not... The themes included in PresentationFramework are included in the SDK (zipped in WPFSamples.zip) under
WPFSamples\Core\AeroTheme
WPFSamples\Core\ClassicTheme
WPFSamples\Core\LunaTheme
WPFSamples\Core\RoyaleTheme
:-D Definitely inspirational stuff!
WPFSamples\Core\AeroTheme
WPFSamples\Core\ClassicTheme
WPFSamples\Core\LunaTheme
WPFSamples\Core\RoyaleTheme
:-D Definitely inspirational stuff!
2006/11/03
WPF - Writing an IMultiValueConverter (update)
What I didn't realize in the previous blog entry regarding the subject was the fact that binding to a DependencyProperty and performing a string.Format blindly could lead to weird results, e.g.:
Could convert to "DependencyProperty.UnsetValue, DependencyProperty.UnsetValue" The solution is pretty simple:
Happy WPFing!
<TextBlock>
<TextBlock.Text>
<MultiBinding
Converter="{StaticResource StringFormatterConverter}"
ConverterParameter="{}{0}, {1}">
<Binding Path="Customer.Lastname" />
<Binding Path="Customer.Firstname" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Could convert to "DependencyProperty.UnsetValue, DependencyProperty.UnsetValue" The solution is pretty simple:
bool argumentsAreNullOrEmpty = true;
// Check whether all values are null or unset.
foreach (object value in values)
{
if (value != null && value != DependencyProperty.UnsetValue)
{
argumentsAreNullOrEmpty = false;
break;
}
}
// If we have all empty or null arguments, we do nothing.
if (argumentsAreNullOrEmpty)
{
return Binding.DoNothing;
}
Happy WPFing!
2006/11/02
CAB: load your modules from your App.Config
In CAB, there's a 'special voodoo magic' file which defines which modules are to be loaded in the IoC process. The file is called ProfileCatalog.xml by default.
We're currently using CAB in combination with an XBAP application. This leads to deployment issues, as ProfileCatalog.xml will be deployed in to a folder called Data by default and the modules configured in the file cannot be located. You can work around this issue, but I took the issue as an opportunity to replace the default implementation of IModuleEnumerator in CAB with my own.
My implementation looks at the App.Config file for it's modules. All that's needed to use the new enumerator is to configure it:
The code for this behaviour will be available here in a few hours...
We're currently using CAB in combination with an XBAP application. This leads to deployment issues, as ProfileCatalog.xml will be deployed in to a folder called Data by default and the modules configured in the file cannot be located. You can work around this issue, but I took the issue as an opportunity to replace the default implementation of IModuleEnumerator in CAB with my own.
My implementation looks at the App.Config file for it's modules. All that's needed to use the new enumerator is to configure it:
<configSections>
<section
name="SolutionProfile"
type="BankTellerModule.Services.ModulesSection, BankTellerModule" />
<section
name="CompositeUI"
type="Microsoft.Practices.CompositeUI.Configuration.SettingsSection, Microsoft.Practices.CompositeUI" />
</configSections>
<CompositeUI>
<services>
<add
serviceType="Microsoft.Practices.CompositeUI.Services.IModuleEnumerator, Microsoft.Practices.CompositeUI" instanceType="BankTellerModule.Services.ConfigurationModuleEnumerator, BankTellerModule" />
</services>
</CompositeUI>
<SolutionProfile>
<Modules>
<add assemblyFile="BankTellerModule.dll" />
</Modules>
</SolutionProfile>
The code for this behaviour will be available here in a few hours...
ClickOnce deployment of XBAP application to remote IIS
To ensure you don't encounter the issues we encountered, make sure you set the option "Rename all application files using the .deploy file extension".
You can find the option under Project properties -> Publish -> Options.
If you don't use this option and your IIS is hardened, as it should be, you'll see your assemblies being rejected (404.2 return code in you W3 log files). The .2 subcode means the server is not allowed to serve .dll files.
The deployment renames the files from e.g. BankTellerModule.dll -> BankTellerModule.dll.deploy. This ensures the files get served by IIS. Locally, the files are stored in C:\Documents and Settings\<user account>\local Settings\Apps\2.0 and renamed back to .dll.
You can find the option under Project properties -> Publish -> Options.
If you don't use this option and your IIS is hardened, as it should be, you'll see your assemblies being rejected (404.2 return code in you W3 log files). The .2 subcode means the server is not allowed to serve .dll files.
The deployment renames the files from e.g. BankTellerModule.dll -> BankTellerModule.dll.deploy. This ensures the files get served by IIS. Locally, the files are stored in C:\Documents and Settings\<user account>\local Settings\Apps\2.0 and renamed back to .dll.
ClickOnce publishing XBAP applications and F5 debugging not working
Some times, when you publish an XBAP application using ClickOnce, you cannot debug the same application afterwards using F5. This is due to a change to the project settings (debug tab). The extension is removed from the command-line argument to PresentationHost.exe (-debug .xbap). Insert the .xbap extension and things work alright again.
2006/10/29
Supporting WPF XML Browser Applications (XBAP) in a CompositeUI Application Block application
Currently, we’re building a real application in WPF for a local government. The application uses the MVC pattern quite heavily, so I did a test to see whether we could use the Composite UI Application Block (CAB, read more about it’s architecture and use, download it from MSDN, play with the labs to understand it) to enable easier composability in order to distribute our use cases more easily throughout the development team, decouple logic from UI even more, etc.
It turns out the CAB currently doesn’t support WPF. After some searching, I discovered Kent Boogaart from Australia (thanks Dion!) is busy writing a support layer for WPF in CAB. So far, so good. Testing the download he provided, however, revealed the implementation doesn’t support so-called XBAP applications.
XBAP application are browser hosted WPF applications and are different in some aspects to ‘traditional’ WPF applications. For instance, the Application object which is the root for all WPF applications, doesn’t allow you to call the Run method if the WPF application is running within a browser. This is because of the asynchronous nature of the webbrowser.
After a few simple modifications to Kent’s code and a little more trickery, I had the BankTeller application working in XBAP. Well, sort of… The thing with CAB is that it assumes synchronous applications. An example of this (taken straight from Microsoft.Practices.CompositeUI.CabApplication):
As I explained earlier, XBAP applications take offense to calling the Run method on their Application, they’re asynchronous. In order to make XBAP applications work, a few modifications have to be made to CAB itself.
If you’re interested in more detail about the changes I made and how I did this, please read the whitepaper I wrote, which can be downloaded here.
A special thanks to Kent Boogaart for creating the WPF port for CAB, without which I wouldn’t have been able to put CAB to use in my WPF scenario!
It turns out the CAB currently doesn’t support WPF. After some searching, I discovered Kent Boogaart from Australia (thanks Dion!) is busy writing a support layer for WPF in CAB. So far, so good. Testing the download he provided, however, revealed the implementation doesn’t support so-called XBAP applications.
XBAP application are browser hosted WPF applications and are different in some aspects to ‘traditional’ WPF applications. For instance, the Application object which is the root for all WPF applications, doesn’t allow you to call the Run method if the WPF application is running within a browser. This is because of the asynchronous nature of the webbrowser.
After a few simple modifications to Kent’s code and a little more trickery, I had the BankTeller application working in XBAP. Well, sort of… The thing with CAB is that it assumes synchronous applications. An example of this (taken straight from Microsoft.Practices.CompositeUI.CabApplication):
public void Run()
{
RegisterUnhandledExceptionHandler();
Builder builder = CreateBuilder();
AddBuilderStrategies(builder);
CreateRootWorkItem(builder);
...
rootWorkItem.FinishInitialization();
rootWorkItem.Run();
Start();
// Whoops for non-blocking Start()s
rootWorkItem.Dispose();
if (visualizer != null)
visualizer.Dispose();
}
As I explained earlier, XBAP applications take offense to calling the Run method on their Application, they’re asynchronous. In order to make XBAP applications work, a few modifications have to be made to CAB itself.
- Don’t call Run on the Application class.
- Ensure the correct UI element get’s displayed.
- Remove the dispose calling from the main Run() method of Microsoft.Practices.CompositeUI.CabApplication.
- Implement a correct IDisposable pattern for cleaning up resources once the Application is actually done with them.
- Ensure we call Dispose to clean up used resources.
If you’re interested in more detail about the changes I made and how I did this, please read the whitepaper I wrote, which can be downloaded here.
A special thanks to Kent Boogaart for creating the WPF port for CAB, without which I wouldn’t have been able to put CAB to use in my WPF scenario!
2006/10/17
WPF - Using CompositeCollection to combine multiple sources for databinding
As I said before, I'll blog about things I learn and find interesting... ;-) Today, I realized there's a very easy way to combine different sources for a binding. It's done using what's called a CompositeCollection. Here's how it works:
We now have the two CollectionViewSource instances up and running. For example purposes, they're looking at the same datasource, but this could be anything else. Now, let's create a simple ListBox control which binds to both these CollectionViewSource instances and adds constant items..
It's as easy as that. The output would be:
My Constant Item
$10.00
$9.00
Book 1
Book 3
Just like the MergedDictionaries within the ResourceDictionary, you can easily combine different sources of data and bind to those sources, even TwoWay.
- Create a datasource (XmlDataProvider, ObjectDataProvider, ...)
- Create a CollectionViewSource to look at the data in a specific way
- Bind a control's Source/ItemsSource/... property to the CollectionViewSource
- Add a view 'static' items
- Bind to another view at the same or different data
<XmlDataProvider x:Key="MyData" XPath="/Info">
<x:XData>
<Info xmlns="">
<Item ID="12345" Name="Book 1" Price="$10.00" />
<Item ID="24678" Name="Book 3" Price="$9.00" />
</Info>
</x:XData>
</XmlDataProvider>
<CollectionViewSource x:Key='a' Source="{Binding Source={StaticResource MyData}, XPath=Item/@Price}" />
<CollectionViewSource x:Key='b' Source="{Binding Source={StaticResource MyData}, XPath=Item/@Name}" />
We now have the two CollectionViewSource instances up and running. For example purposes, they're looking at the same datasource, but this could be anything else. Now, let's create a simple ListBox control which binds to both these CollectionViewSource instances and adds constant items..
<ListBox>
<ListBox.ItemsSource>
<Binding>
<Binding.Source>
<CompositeCollection>
<ListBoxItem>My Constant Item</ListBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource a}}" />
<CollectionContainer Collection="{Binding Source={StaticResource b}}" />
</CompositeCollection>
</Binding.Source>
</Binding>
</ListBox.ItemsSource>
</ListBox>
It's as easy as that. The output would be:
My Constant Item
$10.00
$9.00
Book 1
Book 3
Just like the MergedDictionaries within the ResourceDictionary, you can easily combine different sources of data and bind to those sources, even TwoWay.