05 March 2011

Easy access to WMAppManifest.xml App properties like version and title

Every Windows Phone 7 application must have an Application Manifest file called WMAppManifest.xml. In this you must state title, author, version and some other things. A quote from the link above:

“The primary purpose of this file is the following:

  • The Windows Phone Marketplace application submission process uses information from the manifest file. The manifest file supports the submission of applications to the Windows Phone Marketplace (including certification), device marketplace filtering, marketplace-to-device deployment, and device execution.
  • The information from the manifest file is used as the application metadata that will be stored in the application database.”

For some reason it’s not common knowledge that information in this file can accessed runtime as well. This can come in very handy, for example for showing a consistent app title and even more important, it’s version number. There are some examples of this to be found on the web but they are not very clear or pretty limited, so I cobbled together this little helper class to make retrieving attributes from the App tag a little easier:

using System.Collections.Generic;
using System.Xml.Linq;

namespace LocalJoost.Utilities
{
  /// <summary>
  /// A helper class to easily retrieve data from the WMAppManifest.xml
  /// App tag
  /// </summary>
  public class ManifestAppInfo
  {
    public ManifestAppInfo()
    {
    }

    static Dictionary<string, string> _properties;

    static Dictionary<string, string> Properties
    {
      get
      {
        if (null == _properties)
        {
          _properties = new Dictionary<string, string>();
          var appManifestXml = XDocument.Load("WMAppManifest.xml");
          using (var rdr = appManifestXml.CreateReader(ReaderOptions.None))
          {
            rdr.ReadToDescendant("App");
            if (!rdr.IsStartElement())
            {
              throw new System.FormatException(
                 "App tag not found in WMAppManifest.xml ");
            }
            rdr.MoveToFirstAttribute();
            while (rdr.MoveToNextAttribute())
            {
              _properties.Add(rdr.Name, rdr.Value);
            }
          }
        }
        return _properties;
      }
    }

    public string Version
    {
      get { return Properties["Version"]; }
    }

    public string ProductId
    {
      get { return Properties["ProductID"]; }
    }

    public string Title
    {
      get { return Properties["Title"]; }
    }

    public string TitleUc
    {
      get { return !string.IsNullOrEmpty(Title) ? 
                     Title.ToUpperInvariant() : null; }
    }

    public string Genre
    {
      get { return Properties["Genre"]; }
    }

    public string Description
    {
      get { return Properties["Description"]; }
    }

    public string Publisher
    {
      get { return Properties["Publisher"]; }
    }
  }
}

I got some feedback from Matthijs Hoekstra  on the Standard About Page that it actually showed the version number of the dll in which the AboutViewModel is stored, and not that of the app. This is because Assembly.GetExecutingAssembly() is used. There is also Assembly.GetCallingAssembly() but that shows the version of System.Windows.dll. What we need is Assembly.GetEntryAssembly() but that’s not available in the current version of the Windows Phone 7 framework. But we can now adapt the first part of the AboutViewModel as follows:

public class AboutViewModelBase : ViewModelBase
{
  private ManifestAppInfo _manifestAppInfo;
  public void LoadValuesFromResource<T>()
  {
    _manifestAppInfo = new ManifestAppInfo();
    var targetType = GetType();
    var sourceType = typeof(T);
    foreach (var targetProperty in targetType.GetProperties())
    {
      var sourceProperty = sourceType.GetProperty(targetProperty.Name, 
	     BindingFlags.Static | BindingFlags.Public);
      if (sourceProperty != null)
      {
        if (targetProperty.CanWrite)
        {
          targetProperty.SetValue(this, 
		    sourceProperty.GetValue(null, null), null);
        }
      }
    }
  }
  /// <Summary>A string value for the AppTitle</Summary>
  public string AppTitle
  {
    get
    {
      if (DesignerProperties.IsInDesignTool)
      {
        return "APPLICATION TITLE";
      }
      return _manifestAppInfo.Title;
      
    }
  }
  
  public string ApplicationVersion
  {
    get
    {
      if (DesignerProperties.IsInDesignTool)
        return "version x.x.x";

      var version = _manifestAppInfo.Version;
      return version.Substring(0, version.LastIndexOf("."));
    }
  }
}

And have the ViewModel show the version from the Application Manifest file regardless of actual assembly versions - as well as the title. You can even binding directly to its properties.

Now you might not want to use all of its properties directly in text – if your App Title shows up differently in different languages you still might want to get that from a resource file. That is why the LoadValuesFromResource method now checks if a property is writable. If you omit that check, it will try to write to every property for which a key with the same name can be found in the resource file – and if that’s now read-only, it will crash.

1 comment:

Andy said...

Hey Joost, thanks for the example. That was exactly I was searching for.