DeutschEnglish

Sep 02

Reacting to screen rotation from within a Store App

Although this topic is well documented there are still a few important things to keep in mind, so I decided to put together a summary and point out a few potential pitfalls.

App-wide orientation setting:

We all know the Supported orientations section in every Store App’s manifest:

TemplateWizards error 
 
 
 
 
 
 

When selecting either none or all of the four options, the app will support all orientations, by selecting a subset the set of orientations supported by the app can be reduced.

Since the app manifest is actually an XML file, the same setting can also be defined directly in XML code:

<Package xmlns:m="http://schemas.microsoft.com/appx/2013/manifest">
	...
	<Applications>
		<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="MyApp.App">
			<m:VisualElements DisplayName="MyApp" ...>
				<m:SplashScreen Image="Assets\SplashScreen.png" />
				<m:InitialRotationPreference>
					<m:Rotation Preference="portrait" />
					<m:Rotation Preference="landscape" />
				</m:InitialRotationPreference>
			</m:VisualElements>
		</Application>
	</Applications>
</Package>

Orientation setting per page:

What if – although the app in general may support all orientations – a single page shall be only presented in portrait mode?

We can change the app’s orientation at runtime, either by setting the AutoRotationPreferences property, or by directly calling the SetDisplayAutoRotationPreferences function:

  • In Windows 8.0 Store Apps, the AutoRotationPreferences property is defined in the Windows.Graphics.Display.DisplayProperties class, and can be set to one or several values of the Windows.Graphics.Display.DisplayOrientations enumeration:

    DisplayProperties.AutoRotationPreferences = DisplayOrientations.Portrait;
    

    Multiple values can be combined using bitwise OR operator:

    DisplayProperties.AutoRotationPreferences = DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped;
    

    The preferences can be reset to all orientations by setting it to DisplayOrientations.None.

  • In Windows 8.1 and higher, DisplayProperties.AutoRotationPreferences is marked deprecated. Instead, the DisplayInformation class should be used. The syntax stays the same:

    DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped;
    
  • An alternative that should work in all versions is to directly invoke the SetDisplayAutoRotationPreferences function that needs to be imported from User32.dll:

    [Flags]
    private enum ORIENTATION_PREFERENCE
    {
    	ORIENTATION_PREFERENCE_NONE = 0x0,
    	ORIENTATION_PREFERENCE_LANDSCAPE = 0x1,
    	ORIENTATION_PREFERENCE_PORTRAIT = 0x2,
    	ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4,
    	ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8
    }
    
    [DllImport("User32.dll")]
    extern static bool SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE orientation);
    
    SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE.ORIENTATION_PREFERENCE_PORTRAIT);
    

    Even in this case, the syntax for allowing multiple orientations uses the bitwise OR operator:

    SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE.ORIENTATION_PREFERENCE_PORTRAIT |
                                      ORIENTATION_PREFERENCE.ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED);
    

    To reset preferences to all orientations, use ORIENTATION_PREFERENCE.ORIENTATION_PREFERENCE_NONE.

Good to know:

  • All of the above approaches (setting AutoRotationPreferences as well as calling SetDisplayAutoRotationPreferences) do not only affect the current page, but in general simply change the whole app’s orientation preferences. Therefore we need to reset everything when leaving the page. Typically, you would store the current setting when navigating to a page that shall support different orientation types than the rest of the application, then set the page’s desired orientation modes, and reset them to the original settings when leaving the page:

    private DisplayOrientations _appOrientations;
    
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
    	base.OnNavigatedTo(e);
    	_appOrientations = DisplayInformation.AutoRotationPreferences;
    	DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;
    }
    
    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
    	base.OnNavigatedFrom(e);
    	DisplayInformation.AutoRotationPreferences = _appOrientations;
    }
    
  • Since the app’s orientation preferences are overwritten, the new preferences need not necessarily be a subset of the original, app-wide settings. In fact, it is possible to create an app that only runs in portrait mode:

    <m:InitialRotationPreference>
    	<m:Rotation Preference="portrait" />
    </m:InitialRotationPreference>
    

    …but contains one page that supports that runs in landscape orientation:

    DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;
    

    (although I can’t think of a realistic use case for such a scenario at the moment…)

  • Finally (and that one really caused me a headache): Windows can only detect screen rotations when running on a device that features an accelerometer, therefore also limiting app orientations will only work on such devices! Unfortunately, not even the simulator supports this: Even though it provides the rotate left and rotate right buttons, the content will always adopt to the orientation change no matter what is set as orientation preference, so for testing you’ll need to get a physical tablet device…

Permanent link to this article: http://www.mobilemotion.eu/?p=1586&lang=en

Aug 20

Authenticated network share access from .NET

Thanks to the c# methods in the System.IO namespace accepting UNC paths, it’s rather easy to access files and directories located on a remote machine / server. For example, copying a remote file to the local machine is a one-liner:

using System.IO;
File.Copy(@"\\remotemachinename\path\filename.xyz",
          @"C:\path\targetname.xyz");

Authenticated access to network locations:

Compared to how easy this procedure is, things get surprisingly complicated when the user running the program does not have the appropriate rights to access that remote path: Since System.IO.File and System.IO.Directory methods don’t support passing credentials, the preferred solution is to impersonate an authorized user account. To do so, we need to import two methods from the advapi32.dll and kernel32.dll libraries:

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
	int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

[DllImport("kernel32.dll")]
private static extern Boolean CloseHandle(IntPtr hObject);

The LogonUser method represents the core impersonation functionality, the second one is necessary only for clean disconnect after all file operations have successfully been processed. In addition, we need an instance of WindowsImpersonationContext. This class implements the IDisposable interface, meaning that it can be used within a using block.

Now, let’s combine those three components! The necessary steps are:

  1. Create a user token that represents the authorized user account
  2. Create a WindowsImpersonationContext based on this user token, in order to impersonate the authorized user account
  3. While using this impersonation context, process all the desired file and directory operations
  4. Revert the impersonation, and close the user token

For example, to list all files located within a certain directory on a remote network share, this would be done as follows:

IntPtr token = IntPtr.Zero;
var success = LogonUser("username", "domainname", "password",
	2, 0, ref token);
if (success)
{
	using (WindowsImpersonationContext person = new WindowsIdentity(token).Impersonate())
	{
		string[] allImgs = Directory.GetFiles(@"\\remotemachine\share\folder");
		
		person.Undo();
		CloseHandle(token);
	}
}

We’re passing the authorized user account’s user name, password, and domain name to the LogonUser method, followed by the value 2 which represents the LOGON32_LOGON_INTERACTIVE logon type (further information about the different logon types can be found on the LogonUser MSDN documentation page).

Impersonation without borders:

Seems not too difficult so far, but what if the remote machine is located outside the domain? In this case, there are three things we need to take into account:

  • Since there is no domain name associated with the target user name, we pass the remote machine’s name to the LogonUser method instead
  • The LOGON32_LOGON_INTERACTIVE logon type will not work in that case, instead we use the LOGON32_LOGON_NEW_CREDENTIALS logon type, represented by the value 9
  • Althouth the LogonUser method returns a bool value, this return value will always be true when using the LOGON32_LOGON_NEW_CREDENTIALS type even when the user could not be successfully authenticated, therefore we need to check for exceptions on the first file / directory operation processed within the impersonation context

Our code sample changes to the following:

IntPtr token = IntPtr.Zero;
LogonUser("username", "remotemachine", "password",
	9, 0, ref token);
using (WindowsImpersonationContext person = new WindowsIdentity(token).Impersonate())
{
	try
	{
		string[] allImgs = Directory.GetFiles(@"\\remotemachine\share\folder");
	}
	catch (IOException e)
	{
		//TODO logon error?
	}
	finally
	{
		person.Undo();
		CloseHandle(token);
	}
}

Additional thoughts:

Take into account that the impersonation process might take same time, so the whole procedure should be run in a separate thread or asynchronous task, in order to not block any UI thread.

In addition, if retrieving a list of file names as in the example above, remember that you’ll not be able to actually access any of these files after closing the impersonation context. Therefore, the recommended approach is to always directly open files and load their content into memory, or (since it might be necessary to transfer memory object to the UI thread later on) simply copy them to the local disk for later access.

Permanent link to this article: http://www.mobilemotion.eu/?p=1582&lang=en

Aug 06

NuGet references in Visual Studio project templates

Let’s assume you want to create a Visual Studio project template for Windows 8 Universal App projects. No matter if you use the Export template wizard or create the template by hand, you’ll probably end up with a *.vstemplate manifest file looking similar to the following:

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
	<TemplateData>
		<Name>Multi-Platform Application Core using MVVMbasics</Name>
		...
	</TemplateData>
	<TemplateContent>
		...
	</TemplateContent>
</VSTemplate>

To integrate any NuGet package dependencies into the template (so that all NuGet packages the project depends on are installed automatically when applying the template), you might add something like the following to the VSTemplate node:

<WizardExtension>
	<Assembly>Microsoft.VisualStudio.WinRT.TemplateWizards, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
	<FullClassName>Microsoft.VisualStudio.WinRT.TemplateWizards.CreateProjectCertificate.Wizard</FullClassName>
</WizardExtension>
<WizardData>
    <packages>
        <package id="YourPackage" version="1.2.3" />
    </packages>
</WizardData>

This works well as all the dependencies are copied to the new project, and the NuGet dependencies are restored. However, since version 12 of the TemplateWizards assembly is referenced, as soon as you try to use the project template in Visual Studio 2015, project creation will fail with the following error:

TemplateWizards error 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

The more versatile solution is to reference NuGet’s own WizardExtension:

<WizardExtension>
	<Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
	<FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName>
</WizardExtension>

NuGet.VisualStudio.TemplateWizard is a wrapper that utilizes the proper wizard version, no matter which project type or Visual Studio version is currently used.

An additional advantage of this solution is that it works not only with WinRT projects, but with all project types. This means when creating additional project templates for Windows Phone / WPF / Console / etc. projects, you can use the same <WizardExtension>
markup for all of them.

Permanent link to this article: http://www.mobilemotion.eu/?p=1575&lang=en

Aug 03

MVVMbasics 2.2.6 now supports Visual Studio 2015 and C# 6

Two updates to the MVVMbasics framework have recently been published. The latest version 2.2.6 now allows automatic binding of Commands to conditions via the MvvmCommandAutobinding, and supports Visual Studio 2015 and C# 6. The new version is available as NuGet package, and an updated version of the Project Templates will also be released shortly.    

Permanent link to this article: http://mvvmbasics.mobilemotion.eu

Jul 24

Resizing windows through data binding

Today I tried (and, on the first attempt, failed) to dynamically set a WPF window’s Width and Height properties through data binding. Let’s assume you’d like to set a window’s width from its Viewmodel, by specifying an int property there:

private int _windowWidth;
public int WindowWidth
{
	get { return _windowWidth; }
	set
	{
		_windowWidth = value;
		RaisePropertyChanged("WindowWidth");
	}
}

and binding the window’s Width property to this value:

<Window Title="MainWindow" Width="{Binding WindowWidth}" Height="350" x:Class="... >

No matter if you assign a numeric value to the WindowWidth in the Viewmodel’s constructor, or at a later point in time: Although the property setter is executed and the PropertyChanged is raised, the window won’t resize. It seems that the window doesn’t request its size at startup, and a change to the binding does not trigger repainting of the window.

To avoid this behavior and force the window to be resized when changing the data binding value, there are two options:

  • Instead of the Width property, bind the window’s MinWidth and MaxWidth to the desired Viewmodel property. This forces the window’s size to be reevaluated on each property changed (on startup, the window is assigned initial width and height values – these values are compared to the minimum and maximum size values, and since they are either smaller than the minimum value or larger than the maximum value, the window size is adapted).

    Obviously, by setting minimum and maximum size to the same value, the user won’t be able to resize the window. However, if you’re aiming at creating a fixed-size window anyway, this might be the proper solution.

  • Alternatively, create a two-way binding:

    <Window Title="MainWindow" Width="{Binding WindowWidth, Mode=TwoWay}" ... >
    

    This approach also forces the binding expression to be evaluated and the window to be repainted, adopting the desired size.

Permanent link to this article: http://www.mobilemotion.eu/?p=1552&lang=en

Jul 01

Adding WPF windows to a WinForms project

I’m currenlty working on extending a legacy project with multiple new modules. The original project was started as a WinForms application and contains hundreds of existing forms which we decided to not change due to lack of resources. In contrast, to ensure that at least the newly created modules are state of the art, I’d really like to create those using WPF. However, when trying to add a new WPF window to the project in Visual Studio it turns out this is not possible – I’m only provided with the option to create a WPF user control:

Add WPF User Control

After playing around a bit, I found out there are two ways of making WinForms projects work with WPF windows:

  1. The first option is to actually add a WPF User Control using Visual Studio’s Add New Item wizard, and afterwords convert it to a window. This can be done by

    • replacing the <UserControl ...> tag with <Window ...>, and (if desired) add a Title attribute (don’t forget to change the closing tag also) – the namespaces need not ne adapted -, and

    • even in code behind, make the class inherit from Window instead of UserControl.

    This approach is the easiest option if applied to only one or a few windows.

  2. Since this is a cumbersome and error-prone way if applied to larger projects that require multiple windows to be created, there exists an alternative approach:

    • Open the project’s manifest file (the one with the .csproj extension) in any text editor.

    • The top node <Project> usually contains several <PropertyGroup> tags, one for each build configuration and a global one. In the global <PropertyGroup> node (the one without Condition attribute), search for the <ProjectTypeGuids> sub-node or create one if it does not exist. This node should contain two GUIDs: FAE04EC0-301F-11D3-BF4B-00C04F79EFBC, which stands for a C# project, and 60dc8134-eba5-43b8-bcc9-bb4bc16c2548 which stands for WPF. The full line should look as follows:

      <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
      

      (If you’re interested in details, codeproject holds a complete list of potential project GUIDs: http://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs)

    • Reload the project in Visual Studio, and open the Add New Item wizard.

  3. Since the project is now officially classified as WOF project, this wizard should now contain the WPF window option. By the way, since there is no WinForms project GUID that could be overwritten, this approach does not harm the existing project components.

Independent of which approach you take, before being able to reference WPF windows in code, you’ll need to add a reference to System.Xaml to the project:

Add Reference to Windows.Xaml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Now, you can instantiate and open WPF windows from C# code, e.g. from within a WinForms form:

new MyWpfWindow().Show();

By the way, it is also possible to open modal WPF windows on top of a parent WinForm:

new MyWpfWindow().ShowDialog();

Permanent link to this article: http://www.mobilemotion.eu/?p=1537&lang=en

Jun 29

MVVMbasics source code repository moved to GitHub

The MVVMbasics source code repository has moved from Codeplex to GitHub. From now on, all source files are available on the MVVMbasics GitHub page, and all updates will be published there, the old Codeplex repository will not be updates in the future.                                                

Permanent link to this article: https://github.com/mobilemotion/mvvmbasics

Jun 20

Accessing special Windows folders

Windows provides a set of special folder that are useful in a variety of use cases, for example the subfolders of My Documents (images, videos, etc.), the system-wide temp folder (in most cases C:\Windows\Temp), the application data folders under C:\Users\MyUserName\AppData, and so on. The problem is, many of those special folder paths are dependent on the Windows version and the current user’s name and preferences, so the question is: How do we access those folders programmatically from within a .NET application? I’ve collected a few common scenarios:

…from .NET source code:

For some of the special folders, their exact path is stored in environment variables. For example, the TEMP environment variable points to C:\Windows\Temp (or a similar path, depending on the system). When typing %TEMP% into Explorer’s address bar, the variable is resolved and you are redirected to the actual path. Unfortunately, when trying to use a path that contains environment variables as input to one of the System.IO methods, the variables are not resolved automatically:

string path = Path.GetFullPath("%LOCALAPPDATA%/Apps");

is not sufficient. Instead, use the Environment.ExpandEnvironmentVariables method to resolve the variable’s value before actually using the full path:

string path = Path.GetFullPath(Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%/Apps"));

Unfortunately, some of folders are not uniquely identified by an environment variable, as for example the My Documents folder. Most of these are listed in the Environment.SpecialFolder enumeration, such as My Documents, My Pictures, My Videos, etc. To retrieve the actual folder path as string use the Environment.GetFolderPath method:

string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

…as log4net target path:

Another scenario might be to specify a special folder as target directory for log files. When using log4net, e.g. in combination with a RollingFileAppender, using the common environment variable syntax as part of the file name pattern does not work. For example, the following:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
	<file type="log4net.Util.PatternString" value="%LOCALAPPDATA%/Apps/Log.txt" />
	...

creates a new folder called %LOCALAPPDATA% in the application’s bin folder, instead of linking to the current user’s AppData folder. The correct syntax for referencing environment variables in log4net is as follows:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
	<file type="log4net.Util.PatternString" value="${LOCALAPPDATA}/Apps/Log.txt" />
	...

An alternative approach for folder paths that are not stored in environment variables is the use of the envFolderPath directive. For example, to create log files in the My Pictures folder, use the following syntax:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
	<file type="log4net.Util.PatternString" value="%envFolderPath{MyPictures}/Log.txt" />
	...

…within a Store App:

Since Windows 8 Store Apps are very limited regarding file access, there exist special methods for accessing the above-mentioned folders. In contrast to common .NET applications, WinRT’s file and folder manipulation methods operate on StorageFolder and StorageFile objects that hide the actual file or folder path from the developer. To create a StorageFolder object that represents one of the App’s AppData or temp folders, use the predefined Windows.Storage.ApplicationData properties:

StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFolder roamingFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
StorageFolder tempFolder = Windows.Storage.ApplicationData.Current.TemporaryFolder;

Note, however, that these do not exactly match the special folders listed in the other sections above, but rather special subfolders of C:\Users\MyUserName\AppData that are available only to the current App!

These folders can also be referenced from within XAML code, for example to specify some image’s source path, using the following syntax:

<Image Source="ms-appdata:///local/someImage.png"/>
<Image Source="ms-appdata:///roaming/someImage.png"/>
<Image Source="ms-appdata:///temp/someImage.png"/>

A similar approach exists for accessing the My Documents and its subfolders. They are listed as properties of type StorageFolder in the KnownFolders class, for example:

StorageFolder folder = Windows.Storage.KnownFolders.DocumentsLibrary; // the My Documents folder
StorageFolder folder = Windows.Storage.KnownFolders.PicturesLibrary; // the My Pictures folder

Note that you need to activate the appropriate capabilities in the App’s manifest file, e.g. the Pictures Library capability for accessing the PicturesLibrary folder, otherwise an UnauthorizedAccessException will be thrown at runtime!

Permanent link to this article: http://www.mobilemotion.eu/?p=1519&lang=en

Jun 07

Scheduled live tile update

Live tiles are one of the main new concepts of Windows 8. Obviously, for the optimum user experience, live tiles should update regularly in oder to actually display “live” data. Technically, this is an interesting field: Updating a tile from within an App is straightforward, but not that interesting since the App is active (and shown to the user) anyway. It would be more important to regularly update live tiles while the App is not running – this is typically achieved either by using push notifications, or by updating the tiles from within a background task.

When sticking to the second approach, we’re facing one of the major Store App limitations: Background tasks may run only every 15 minutes, and are given a very limited time to execute. If you’d like to update some live tile every 3 minutes, build a background task that does the update logic, and try to register this background task to your App:

var builder = new BackgroundTaskBuilder
{
	Name = "MyBackgroundTask",
	TaskEntryPoint = "BackgroundTask.MyBackgroundTask"
};
IBackgroundTrigger trigger = new MaintenanceTrigger(3, false); // start every 3 minutes
builder.SetTrigger(trigger);

…you’ll be presented an ArgumentException at runtime (Value does not fall within the expected range.) – registerering a background task to be triggered every 3 minutes is not allowed, you need to specify a minimum value of 15 (minutes) as the trigger interval.

The solution here is to use ScheduledTileNotifications. This allows us to run the background task only once and plan several tile updates ahead of time. Let me illustrate that with the help of a small sample application! To any (new or existing) Store App, we add a background task that’s triggered every 15 minutes:

Registering a scheduled background task 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

var builder = new BackgroundTaskBuilder
{
	Name = "MyBackgroundTask",
	TaskEntryPoint = "BackgroundTask.MyBackgroundTask"
};
IBackgroundTrigger trigger = new MaintenanceTrigger(15, false);
builder.SetTrigger(trigger);
IBackgroundCondition condition = new SystemCondition(SystemConditionType.InternetAvailable);
builder.AddCondition(condition);
builder.Register();

(Since we’ll populate the live tile with images from the Internet, I’ve added the InternetAvailable condition to the background task, this has nothing to do with the update scheduler mechanism.)

Within the background task’s main class, we first create the common structure needed for all background tasks:

public sealed class MyBackgroundTask : IBackgroundTask
{
	private const int UpdateInterval = 10;

	public void Run(IBackgroundTaskInstance taskInstance)
	{
		BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
		CreateSchedule();
		deferral.Complete();
	}

	public void CreateSchedule()
	{
		//TODO
	}
}

Two things are to be noted here:

  • the CreateSchedule() method that will contain the actual business logic for planning live tile updates, and
  • the UpdateInterval variable that will be used throughout the CreateSchedule() method – the overall idea is to update one live tile every 10 seconds with a different image downloaded from the web.

Now, let’s fill in the missing CreateSchedule() method! This is done in four parts:

  1. Collect all the information to be presented as the live tile’s content over the next 15 minutes. In my example, I’ll use a short static list of image URLs, in a real life application this might come from App settings, a database, etc. and will contain a lot more items:

    var urls = new string[]
    {
    	"http://my.dummywebsite.com/image1.png",
    	"http://my.dummywebsite.com/image2.png",
    	"http://my.dummywebsite.com/image3.png"
    };
    

    Make sure that the images are not too big to be displayed as tile content – for the best user experience, use images with a pixel size that matches the tile template’s size exactly!

    Each of those items is to be presented for 10 seconds, followed by the next item, and so on, until the last one is reached – after that, we’ll restart with the first one, until (after approximately 15 minutes) the background task is executed again to override the current update schedule.

  2. Clean the previously set update schedule. Obviously, this is irrelevant when executing the background task for the first time, but it’s really important on all the subsequent iterations.

    var tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
    foreach (var item in tileUpdater.GetScheduledTileNotifications())
    {
    	tileUpdater.RemoveFromSchedule(item);
    }
    
  3. Create one initial live tile content that is shown immediately, until the first scheduled tile update sets in. In our example, we simply use a large square tile containing only one image, but the principle is applicable to all available tile templates, of course:

    // Create tile which is shown instantly
    DateTime now = DateTime.Now;
    var documentNow = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare310x310Image);
    var counter = 0;
    documentNow.GetElementsByTagName("image")[0].Attributes[1].InnerText = urls[0];
    tileUpdater.Update(new TileNotification(documentNow)
    {
    	ExpirationTime = now.AddSeconds(UpdateInterval + 1)
    });
    ++counter;
    

    Although we’ll specify all the tile updates to be executed after 10 seconds, I tend to add an extra second to each tile’s expiration date just to be on the safe side and avoid an empty tile shown for half a second) if the next tile content is a bit delayed.

  4. …and now the most interesting part: Schedule all the other tile updates! No matter how long the list of image URLs is, we always specify as many tile updates necessary to fill the time until the background task’s next execution. In our case this means a tile update every 10 seconds for a time span of 15 minutes, resulting in a total number of 90 scheduled tile updates.

    // Create scheduled tiles
    DateTime planTill = now.AddMinutes(16);
    DateTime updateTime = now.AddSeconds(UpdateInterval);
    
    for (var startPlanning = updateTime;
    	startPlanning < planTill;
    	startPlanning = startPlanning.AddSeconds(UpdateInterval))
    {
    	// Start with the first image again if the last one is reached
    	if (counter == urls.Length)
    		counter = 0;
    
    	var documentPlanned = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare310x310Image);
    	documentPlanned.GetElementsByTagName("image")[0].Attributes[1].InnerText = urls[counter++];
    
    	var scheduledNotification = new ScheduledTileNotification(documentPlanned, new DateTimeOffset(startPlanning))
    	{
    		ExpirationTime = startPlanning.AddSeconds(UpdateInterval + 1)
    	};
    	tileUpdater.AddToSchedule(scheduledNotification);
    }
    

    By the way, the line DateTime planTill = now.AddMinutes(16); is not a typo. Windows generally treats background tasks with low priority, if something more important is to be done when a background task should actually be executed, it might be delayed by the system. For this reason, I recommend scheduling a longer period of time than necessary.

Permanent link to this article: http://www.mobilemotion.eu/?p=1524&lang=en

May 24

Retrieving logon information in a Store App

The short story:

When using the UserInformation.GetDomainNameAsync() method from within a Windows Store App, make sure that the app’s Enterprise Authentication capability is set, and be prepared for exceptions…

The long story:

As part of the line-of-business app I was recently blogging about, one requirement was to retrieve the Windows user name of the currently logged in user and pass it on to some web service.

Basically, all available Windows logon information can be retrieved via the Windows.System.UserProfile.UserInformation class. For example, the user name can easily be accessed by calling the GetDomainNameAsync method which returns the current user’s logon name including the domain name.

Although this sounds easy, on the first try all I got was an unhandled exception, so I looked into the method’s documentation in details and played around a bit. When using this method in one of your applications, note the following:

  • Obviously, the method runs asynchronously and needs to be awaited:

    var username = await Windows.System.UserProfile.UserInformation.GetDomainNameAsync();

  • The user needs to allow usage of the logon information in the machine’s privacy options:
    Privacy options: Allow logon information usage 
     
     
     
     
     
     
     

  • The apps’ Enterprise Authentication capability must be set:
    Store App Capabilities: Enterprise Authentication 
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

  • Just in case, wrap the method call in a try / catch block and care for an alternative solution. Although the GetDomainNameAsync method’s documentation on MSDN states: If access is blocked, this method returns an empty string. This method does not throw an exception. – This is not true, for example if you forget checking the Enterprise Authentication capability, an UnauthorizedAccessException will be thrown!

Permanent link to this article: http://www.mobilemotion.eu/?p=1501&lang=en

Older posts «