Preface
The Microsoft Advertising SDK for Windows 8 allows developers to show ads in their apps. When you look at the walkthrough for putting ads in an app using XAML/C#, you can see that you have to set the ApplicationId
and AdUnitId
property of each AdControl control in your app.
For testing purposes, Microsoft has defined the ApplicationId d25517cb-12d4-4699-8bdc-52040c712cab
and several AdUnitIds (can be found on the Test Mode Values page). These values are required in case you have no ids created in the pubCenter for yourself or in case you want to test your app in the emulator. (Well, on my machine the emulator showed productive ads even though the docs says it does not.)
Since I do not want to change my app every time I switch between the emulator and my dev machine, I was looking for a way to make these setting configurable.
Where to Store the Configuration Data?
In common .NET applications, the answer to this question is very simple: put it in the app.config. Unfortunately, for Windows Store apps the app.config approach is not available, or at least not offered by .NET for Windows Store apps.
So I built my own app.config mimic. Please have a look at the post Mimic app.config in Windows Store Apps for further details.
How to Change the Values During Runtime?
My first try was to define the AdControls in the XAML file and change both ApplicationId
and AdUnitId
in the constructor of the page’s class right after the call to PageClass.InitializeComponent
. After receiving “NoAdAvailable” errors (which shows how important it is to implement an error event handler for AdControls), I had a look at the documentation.
In the remarks to the AdUnitId
property the docs says that “This property can be set only when the AdControl is instantiated using the default constructor. Once set, this property cannot be modified.” The ApplicationId
can be changed whenever you want. But that won’t help, because ApplicationId
and AdUnitId
are strongly tied together. Means, an AdUnitId
is only valid in conjunction with its corresponding ApplicationId
.
This means I cannot change the AdUnitId of all the AdControls that are defined in the XAML. Bad news? Not that bad – because we can also create UI controls during runtime, not only in XAML!
The AdControl Replacement Approach
After playing around a little bit I was choosing a replacement approach. This means that I define AdControls in XAML, and replace them during page initialization with those AdControls having the correct values for Application- and AdUnitId.
I prefer this approach because it offers the ability to check the look of the page also during design time, not only at runtime. So it is much easier to see if the page looks good. This requires me to set the (matching) height and width of the AdControls properly both in the XAML and configuration.
There is one prerequisite to use the replacement approach the way I implemented it: the AdControl to be replaced has to be a child control of a Panel
(or derived class). Otherwise it is not possible to put the new AdControl at the place where the old one was.
The definition of the required config values is encapsulated by the class AdControlConfigData
. The class looks like this:
public class AdControlConfigData { [XmlAttribute] public string AdControlName { get; set; } [XmlAttribute] public string AdUnitId { get; set; } [XmlAttribute] public double Height { get; set; } [XmlAttribute] public double Width { get; set; } }
The corresponding serialized data in the app.config looks like this:
<AdControlConfigData AdControlName="AdControl300x600" AdUnitId="10043030" Width="300" Height="600"/>
The value of the ApplicationId is not part of this container. It is defined as a simple and single value in the app.config, since it is the same for all AdControls.
Replace the AdControls
In the sample code, I created a class called AdControlContainingPage
, which is derived from LayoutAwarePage. This class is the base class of all pages containing AdControls. It holds all the functionality required to replace them, plus a basic handler for AdControl errors.
The XML config data is read by the ConfigurationManager
class introduced in the Mimic app.config in Windows Store Apps post, also the XmlDeserialize
used later.
For the sample app, the content of the app.config looks like this:
<?xml version="1.0" encoding="utf-8" ?> <!-- Configuration settings for Debug Build --> <configuration> <appSettings> <!-- Use Microsoft Advertising Services Test ApplicationId --> <add key="AdApplicationId" value="d25517cb-12d4-4699-8bdc-52040c712cab"/> </appSettings> <AdControlConfigList300x600> <AdControlConfigData AdControlName="AdControl300x600" AdUnitId="10043030" Width="300" Height="600"/> </AdControlConfigList300x600> <AdControlConfigList300x250> <AdControlConfigData AdControlName="AdControl300x250" AdUnitId="10043056" Width="300" Height="250"/> </AdControlConfigList300x250> <AdControlConfigListMultiple> <AdControlConfigData AdControlName="AdControl160x600" AdUnitId="10043136" Width="160" Height="600"/> <AdControlConfigData AdControlName="AdControl728x90" AdUnitId="10042998" Width="728" Height="90"/> </AdControlConfigListMultiple> </configuration>
The different AdControlConfigData entities are extracted by the method AdControlContainingPage.GetAdControlConfiguration
, which is shown here:
private static List<AdControlConfigData> GetAdControlConfiguration ( XmlDocument xmlConfig, string sectionName ) { List<AdControlConfigData> configDataList = new List<AdControlConfigData>(); XmlNodeList xmlConfigDataList = xmlConfig.DocumentElement .SelectNodes( String.Format("./{0}/AdControlConfigData", sectionName)); foreach (IXmlNode node in xmlConfigDataList) { AdControlConfigData configData = XmlDeserializer.Deserialize<AdControlConfigData>(node); configDataList.Add(configData); } return (configDataList); }
Now that we have the configuration settings as a list of objects, which is really easy to handle, we can go on to replace the controls. This is done by AdControlContainingPage.ReplaceAdControls
.
private void ReplaceAdControls ( List<AdControlConfigData> adControlConfigDataList, string adApplicationId ) { // iterate over the list of configured AdControls. foreach (AdControlConfigData configData in adControlConfigDataList) { // Find the AdControl to be replaced. AdControl oldAdControl = FindName(configData.AdControlName) as AdControl; // If the oldAdControl is not found, we can't replace it. So try the next one. if (oldAdControl == null) { Debug.WriteLine(String.Format("AdControl name >{0}< not found", configData.AdControlName)); continue; } // Get the parent of the adcontrol. Needs to be a kind of panel. Panel parent = oldAdControl.Parent as Panel; // If the parent is not a panel, try the next one. if (parent == null) { Debug.WriteLine(String.Format("AdControl name >{0}< is not placed inside a panel or derived class. AdControl cannot be replaced.", configData.AdControlName)); continue; } // Keep the position of the old AdControl. int postion = parent.Children.IndexOf(oldAdControl); // and remove it parent.Children.RemoveAt(postion); // Create the new AdControl with the correct ApplicationId and AdUnitId. AdControl adControl = new AdControl() { ApplicationId = adApplicationId, AdUnitId = configData.AdUnitId, // From msdn: This property can be set only when the AdControl is instantiated. Once set, this property cannot be modified. HorizontalAlignment = HorizontalAlignment.Left, Height = configData.Height, IsAutoRefreshEnabled = true, VerticalAlignment = VerticalAlignment.Top, Width = configData.Width }; // Add the error hander adControl.ErrorOccurred += OnAdControlErrorOccurred; // Add the new control to the parent at the correct position. parent.Children.Insert(postion, adControl); } }
The orchestration of these activities is done by AdControlContainingPage.SetupAdControls
, which looks as follows:
protected async void SetupAdControls ( string configSectionName ) { try { // Load the app.config. XmlDocument xmlConfig = await ConfigurationManager.GetConfig(); // Get the AdApplicationId string adApplicationId = ConfigurationManager .GetAppSettingsValue(xmlConfig, "AdApplicationId"); // Extract the AdControl config data from the XML into a object list. List<AdControlConfigData> adControlConfigDataList = AdControlContainingPage .GetAdControlConfiguration(xmlConfig, configSectionName); // Replace the AdControls ReplaceAdControls(adControlConfigDataList, adApplicationId); } catch (Exception e) { Debug.WriteLine(String.Format("Exception occurred:{0}{1}", Environment.NewLine, e)); } }
One should call AdControlContainingPage.SetupAdControls
right after the call of InitializeComponent
of the derived class. In the sample, the derived classes are SamplePage300x250, SamplePage300x600,
and SamplePageMultiple
, which can be found in the SubPages folder of the solution.
The Sample Code
… can be found here. To build and run the sample, you need to have the Microsoft Advertising SDK for Windows 8 installed. Have fun 🙂
Load It From The Windows Store
A slightly extented version of the sample can also be found in the Windows Store.
And finally, some screenshots of the sample app.