Since every Windows Phone application should have some kind of About page, I wanted to see if I could build a generic page that I would reuse in my other apps. These are a few tips I picked up during building such a page:
1. The About page is rarely accessed so I put it in a separate assembly. This way I took some initial load off the main assembly and the About assembly can now easily be shared with other apps.
2. If you want to display the application icon on your About page, there is no need to create new graphics. Depending on your target size, you can choose from either ApplicationIcon.png (62x62) or Background.png (173x173) icons. To put the icon on your About page, use:
<Image Source="/ApplicationIcon.png" Stretch="None" />
And of course you can specify your own size and adjust the stretching attribute.
But don’t worry about the ApplicationIcon.png not being included in the About page project. With application icons being marked as Content, the contents of a Xap should look something like this:
By specifying an external link reference to the ApplicationIcon.png, the icon will be displayed regardless of it not being part of the project that contains it.
3. Static text. This includes titles, labels and text that will likely be the same across every application. If you’re not localizing your apps, you may as be well hard-coding strings into your XAML. But if you are, you would most definitely want to pull them from your resource files. In latter case, the standard localization guidelines apply. Again, don’t worry if your if your localization files are in your main assembly. The application will pick them up correctly regardless (even at design time).
<TextBlock x:Name="PageTitle"
Text="{Binding Strings.AboutTitle,
Source={StaticResource LanguageResources}}"
Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
4. Dynamic text. This includes the version number, application title and other text that will be unique to every application or its version. Some of these texts could be stored in resource files as well, but let’s look at the alternative options.
If you take a look at the AssemblyInfo.cs file of your main project (you’ll find it in the Properties folder), you’ll see it’s populated with some weird assembly attributes you don’t even care about (that’s because you’re setting and changing them from Visual Studio’s dedicated project properties page, but that’s not the point).
The point is that you can access all of these strings from code. All you have to do is get to the main assembly and read its custom attributes (which is what those lines from the AssemblyInfo file really). Here’s how:
Getting the main / entry assembly
The first thing is getting access to the correct – main (or entry) assembly. There are many ways to do that. At least one of those listed below should play nice with your your application architecture :
Assembly.GetExecutingAssembly() will get a reference to an assembly that’s executing that same code.
Assembly.GetCallingAssembly()will get a reference to an assembly of the method that called into this code.
The above methods don’t work well if the call is originating from the same dll. Exploring further…
Deployment.Current.EntryPointAssembly returns a name of the main assembly in pure string. You can use that to load the assembly with Assembly.Load(System.Windows.Deployment.Current.EntryPointAssembly). This will get you the main assembly, but it doesn’t feel right, so I finally settled with:
Application.Current.GetType().Assembly depends on using the Application static class, but since this is only used for a simple About page, that shouldn’t be a significant problem.
Reading the attributes
I created the following helper method to read the attributes off an assembly:
private string GetAttribute<T>(Func<T, string> selector)
where T: Attribute
{
object[] attributes = assembly.GetCustomAttributes(typeof(T), true);
if (attributes.Length == 0)
{
return "N/A";
}
T attribute = attributes[0] as T;
return attribute == null ? "N/A" : selector(attribute);
}
GetCustomAttributes method overload above will return the only specified type of attribute you’re requesting, and the selector Func will read the value off of it. In the example of getting the Version attribute, here’s how you would use the function:
private string version;
public string Version
{
get
{
return version ??
(version = GetAttribute<AssemblyFileVersionAttribute>(a => a.Version));
}
}
For the application title, you would use the AssemblyTitle attribute:
private string title;
public string Title
{
get
{
return title ??
(title = GetAttribute<AssemblyTitleAttribute>(a => a.Title));
}
}
And so on…
And if you’d like, you can even create your own attributes, for example:
[AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
[ComVisible(true)]
public class AssemblyContactAttribute : Attribute
{
public string Email { get; private set; }
public AssemblyContactAttribute(string email)
{
Email = email;
}
}
Put that into the AssemblyInfo.cs file, as seen with other assembly attributes:
[assembly: AssemblyContact("myapp@tozon.info")]
and use the GetAttribute method similar to above examples.
You have just decorated your assembly with a new custom attribute that you can pull out at runtime.
By following those four points I was able to completely isolate my About page from main app(s) and adding the About page in my new apps is a matter of seconds. There is some convention involved of course (like the name of resources reference holding class), but I don’t see this as a problem. What do you think?