In my previous post, I described how to convert Time Zones in WinRT/Store apps. I’ve gone ahead and released a Windows Runtime component to NuGet that provides basic time zone conversion.
For anyone who’s tried to convert a DateTime/DateTimeOffset to another time zone in a Windows Store style app, I’ve put together a helper class that uses some of the Win32 APIs that are allowed in Store apps.
UPDATE 10/16: I've created a NuGet package with a generic version, please use that instead as it already has significant bug fixes.
In the code below, I’m converting all times to be Eastern time, but it can be easily adapted more generically. I’m calling the Win32 functions that take changes in daylight time into account, so it should be accurate for any supplied date.
The TimeZoneKeyNames are in the registry in the following location:
HKEY_LOCAL_MACHINE SOFTWARE Microsoft Windows NT CurrentVersion Time Zones time_zone_name Dynamic DST
The code is based on DateTimeOffset’s as DateTime isn’t allowed as a public type in a Windows Runtime component. Using this code, you can display a list of system time zones. You can also pass in a current DateTimeOffset or DateTime (make sure you have it correctly marked as either UTC or Local) and convert it to a DateTimeOffset in another timezone.
Hopefully this will help someone or can be included as part of a bigger utility library.
Extension SDKs are one of the new ways of distributing components for Metro-style applications. An Extension SDK is similar in concept to a regular assembly reference, but is instead a rich collection of files to cover various configurations and design time scenarios. Beyond a simple file reference, Extension SDKs provide the following:
The full details on how to create an Extension SDK are in the MSDN page linked above, but one thing to note is that by default, VS and MSBuild will only look for Extension SDKs in the following locations:
Naturally, a question arises around storing an Extension SDK alongside their source code instead of in a separate location. There are a few reasons why some people want to do this:
The solution is to put the libraries in version control in a “libs” directory. NuGet does this for existing libraries in a \packages directory at the solution-level; it does not yet support Extension SDKs. The good news is that both Visual Studio 11 and MSBuild already support defining additional locations for Extension SDK’s by overriding the SDKReferenceDirectoryRoot variable. The key is to add the override after the <Import> element near the end of the csproj/vbproj file like this:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v11.0\Microsoft.Windows.UI.Xaml.CSharp.targets" /> <PropertyGroup> <SDKReferenceDirectoryRoot>$(SolutionDir)\libs;$(SDKReferenceDirectoryRoot)</SDKReferenceDirectoryRoot> </PropertyGroup>
With that in place, you can then put your Extension SDK files alongside your solution:
Once there, it will be available in the Visual Studio Add References dialog like any other Extension SDK.
Let’s walk through an example of how we can use the Bing Maps SDK for Metro style apps (beta) as a per-project SDK instead of being installed in either the user profile or program files locations. Initially, most Extension SDKs are likely going to be distributed as either a VSIX or MSI so they can install into the right location. The easiest approach is to install on one machine and then copy the files into your source tree. Then you can then uninstall the SDK if you’d like.
After downloading the Bing Maps SDK from the gallery, install the VSIX file:
Next, lets create a new Bing Maps Application using the template installed by the VSIX:
Once the project is created, if you expand References in the Solution Explorer, you’ll see that Bing has been added as a reference:
Right-clicking and selecting Properties on the reference will show you that it’s an SDK reference and where the file are located:
You can see from the Path that Visual Studio (and MSBuild) are reading the file from \Users\[username]\AppData\Local\Microsoft SDKs\Windows\v8.0\ExtensionSDKs.
Now, lets make our changes so we can store those files alongside our solution.
First, in Explorer, navigate to the folder with your solution in it, then create the following directory structure:
Next, in another Explorer window, go to your local AppData directory in your user profile and navigate to the ExtensionSDKs directory there:
The final step is to add the “libs” path from your solution to the search path for ExtensionSDKs in your project file. Right-click the project in the Solution Explorer and click Unload Project:
Now you can right-click the project again and select Edit Project File. This will bring up the csproj (or vbproj) file up in the XML editor. Scroll to the bottom of the file and you’ll see a line starting with “<Import Project=…”. On the next line, add the following XML:
<PropertyGroup> <SDKReferenceDirectoryRoot>$(SolutionDir)\libs;$(SDKReferenceDirectoryRoot)</SDKReferenceDirectoryRoot> </PropertyGroup>
That will tell Visual Studio and MSBuild to look for additional Extension SDKs in your libs directory before checking the default locations.
Save your changes and then reload the project file by right-clicking the project in the Solution Explorer and clicking Reload Project. If you check the properties for the reference, as before, you’ll now see it resolving to your project path instead of your user profile:
You’re now ready to check your changes in and share them with others. Build systems and other users of your code won’t need to install the extensions as they’ll be using the version alongside your project.
One additional thing to note is that after modifying your project file to include your libs directory, Visual Studio’s Add Reference dialog will automatically pick up any Extension SDKs that reside there:
The Reflection API in .NET 4.5 received an overhaul. It’s easy to miss at first glance as for backwards-compatibility reasons, when you’re targeting .NET 4.5, you’ll still see all of the properties and methods on the Type and Attribute-related classes that you’ve used for years. The easiest way to see the extent of the redesign is to create a new Metro-style application or class library and look at the available properties there (or just use the Object Browser.) When you look at Type, you’ll immediately notice that most of the methods you’ve used in the past are missing – like GetProperties or GetMethods. If you look around, you’ll also see that a frequently-used enum, BindingFlags, has been completely removed.
All of the functionality is still present but has been refactored into two main areas, TypeInfo and a set of extension methods. First, to get access to TypeInfo and its related extension methods, you do need to have a “using System.Reflection”. That adds a GetTypeInfo method to Type and a series of Attribute related ones to PropertyInfo’s, MemberInfo’s, etc. The TypeInfo object provides access to methods, properties, fields, etc., that are declared on the type itself (but not on base classes). It’s the same as using BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly in the older API’s. If you want to have behavior similar to the older GetMethods/GetProperties-type calls, that’s where the RuntimeExtensions methods come in. To use these, you need to add a “using System.Reflection.RuntimeExtensions”. After that, you’ll get access to a series of methods, GetRuntime* that extend Type instances. For the most part, it’s doing the same thing as the original methods, but without letting you specify the binding flags – instead, it uses BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance. It’s up to you to filter out the results if you only want a subset, like instance methods, public, etc. Fortunately, Linq makes this really easy by adding a .Where(…) to the result of the calls.
For the most part, if you’re porting existing code from .NET to Metro, you’ll want to use the GetRuntime* methods as those are the closest to the older ones. You’ll then need to consider the binding flags you were using and add the appropriate Where clause at the end.