DeutschEnglish

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 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

May 20

Store Apps & Private network access

The short story:

To consume a domain-internal WCF service from within a Windows Store App, the app’s Private networks (Client & Server) capability must be set.

The long story:

Recently, I was (once again) coding a line-of-business app for company-internal use. This app connects to a server that resides within our company’s domain to consume a WCF service. The service’s WSDL was accessible through the web browser, and other (console) applications running on the same machine could communicate with the WCF service without problem. This particular Store App, however, failed with the following exception:

There was no endpoint listening at http://myserver/myservice.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.

…with an InnerException saying "Unable to connect to the remote server".

The (obvious) solution:

Unfortunately this exception, although it’s correct of course, is somewhat misleading. I spent quite some time checking the service reference’s configurations, deleting and recreating the service reference, etc. because in any other application I had expected the problem to be somewhere in there. However, the final solution was surprisingly simple: Connecting to the web service failed because the app was not allowed to access the company network. I simply forgot to set the app’s Private networks (Client & Server) capability within its manifest file. After doing so, there were no more connection problems, and I knew I’d never again forget checking App capabilities…

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

Apr 27

Using template parameters in a Store App’s manifest

The short story:

When using parameters while creating Project Templates for Visual Studio, the default parameters (e.g., $safeprojectname$) are only replaced in code files. To enable parameter substitution for other files (e.g., a Windows Store App’s manifest file), set the ReplaceParameters attribute in the template’s .vstemplate file.

The long story:

Recently, I’ve been asked whether it was possible to use template parameters in a Windows Store App’s manifest file. For all of you who need a bit more background information: When exporting a project as template in Visual Studio, certain parts of the source code are replaced by predefined parameters, for example the namespace declaration in all code files is replaces with $safeprojectname$. Later on, when applying the template as a new project, there parameters are substituted with actual values, for example, all occurences of $safeprojectname$ are replaced with the new project’s name.

The problem is: Out of the box, this does not work for .appxmanifest files, although it would make sense to use template parameters in a Store App’s manifest to, for example, customize the app’s display name (by referencing $projectname$) or publisher name (through $registeredorganization$). Technically, this is not a problem since the .appxmanifest file is a simple XML file, so XML attributes and contents could be replaced by $...$ variables. The question is, what to do in order to make Visual Studio actually replace those variables?

The solution:

Technically, the template is just a .zip package that contains all the files that were present in the project that has been exported as template (C / C# / VB source files, manifest file, resources, etc.). When unpacking its contents to some local folder, you will find an additional file that was not part of that project, it is called [ProjectName].vstemplate.

This .vstemplate file acts as the template’s manifest. If you open it using notepad, you’ll find out that it is an XML file that contains:

  • the template’s meta data (name, description, icon, etc.),
  • and all of the projects references.

The latter includes both external references (e.g., to NuGet packages or DLL files), and internal references (meaning all source files that belong to the project). Those internal file references are listed as ProjectItem nodes and are structured within Folder nodes to represent the file system structur. For each ProjectItem, the XML attribute ReplaceParameters is defined – this attribute controls whether template parameters can be used within a certain file (and will be substituted on project creation) or not.

Now, the solution is simple: Find the .appxmanifest file in the list, change its ProjectItem to ReplaceParameters="true", and re-pack all files in a ZIP archive that now can be used as project template!

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

Apr 16

Data binding and image cache

One of the comfortable things in WPF data binding is the fact that a lot of types are converted automatically during binding. One example is the Image control and its Source property: This property is of type ImageSource, but it also accepts string input. To illustrate this, let’s assume that you have some string property in your Viewmodel that points to some local image on your hard drive:

private string _path;
public string Path
{
	get { return _path; }
	set { _path = value; RaisePropertyChanged("Path"); }
}

public MyViewmodel()
{
	Path = @"C:\image.png";
}

The View contains an Image control that binds to this string property:

<Image Source="{Binding Url}" />

The problem with this type of binding is that the image is loaded directly from the file to be rendered on the screen – trying to modify, swap, or even rename the file while the application is running will fail because the file is in use and locked by our WPF application.

The reason for this is that the automatic type conversion from string to ImageSource uses default settings and there is no way to customize it. If we’d manually create a BitmapImage (which inherits from ImageSource and can therefore be used as input to the image’s Source property), we’d set its BitmapCacheOption property to OnLoad, since this loads the image into memory at load time and instantly releases the image file. However, it’s unpleasant to hold an object of type BitmapImage in the Viewmodel (e.g. when targeting multiple platforms and keeping all Viewmodels within a Portable Class Library, the BitmapImage can not even be instantiated within the Viewmodel since it’s WPF specific).

The elegant solution here (as in many other cases) is a binding converter: It converts the Viewmodel’s string property to a platform-specific BitmapImage while resolving the binding expression, and sets the custom cache option during this process:

public class ImageConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		var path = value as string;
		if (path != null)
		{
			var uri = new Uri(path);
			var bitmap = new BitmapImage();
			bitmap.BeginInit();
			bitmap.UriSource = uri;
			bitmap.CacheOption = BitmapCacheOption.OnLoad;
			bitmap.EndInit();
			return bitmap;
		}
		return DependencyProperty.UnsetValue;
	}
}

This solution keeps the Viewmodel clean, while the binding expression in the View’s XAML code needs only be complemented by a short converter reference:

<Image Source="{Binding Url, Converter={StaticResource ImageConverter}}" />

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

Mar 31

[MVVMbasics] Passing custom parameters during event-to-command redirection

Notice

All blog posts with a title that starts with [MVVMbasics] are related to the MVVMbasics framework. If you are only interested in my regular blog posts and not in MVVMbasics, ignore these. To list all articles covering MVVMbasics, go to http://mvvmbasics.mobilemotion.eu/documentation!

In version 2.0 of the MVVMbasics framework, a simple way to redirect a UI event directly to a Viewmodel Command has been introduced, via the EventToCommand keyword and the Command attached property:

<ListBox SelectionChanged="EventToCommand" mvvm:Event.Command="{Binding ListSelectionChangedCommand}" ... />

This can be used as a short cut in many situations to avoid including the Interaction framework (which usually results in long XAML code lines) or invoking Viewmodel Commands from within code-behind event handlers.

With the 2.2 release, this feature has become more flexible: An additional attached property CommandParameter has been introduced that allows passing custom parameters to the target Command instead of the generic event arguments! The CommandParameter can be specified either as fixed value, or through Data Binding:

<ListBox x:Name="List"
         SelectionChanged="EventToCommand"
         mvvm:Event.Command="{Binding ListSelectionChangedCommand}"
         mvvm:Event.CommandParameter="{Binding ElementName=List, Path=SelectedItem}" />
<CustomControl DoubleClick="EventToCommand"
               mvvm:Event.Command="{Binding ActivatedCommand}"
               mvvm:Event.Command="123" />

In the Viewmodel, you instantiate the BaseCommand with a target method that accepts one parameter of type object, just as in the previous version. This parameter, however, will contain the passed parameter. Of course, it is still possible to get the raw event arguments forwarded to the Command by simple omitting the CommandParameter property, as in this case the event args will be automatically used as parameter.

Be aware, though, that you still need to declare the mvvm namespace in the View’s XAML header. In addition, please notice that Windows Phone Silverlight projects do not support the EventToCommand keyword due to a bug in the Silverlight platform. Instead you’ll need to attach the EventToCommand handler to the desired event in C# code-behind, as explained in detail in the original article presenting the MVVMbasics event-to-command binding solution!

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

Mar 22

[MVVMbasics] Attribute-based bindable property declaration

Notice

All blog posts with a title that starts with [MVVMbasics] are related to the MVVMbasics framework. If you are only interested in my regular blog posts and not in MVVMbasics, ignore these. To list all articles covering MVVMbasics, go to http://mvvmbasics.mobilemotion.eu/documentation!

Previous versions of the MVVMbasics framework provided the Set method to help shorten bindable property declarations in Models and Viewmodels:

private string _someText;
public string SomeText
{
	get { return _someText; }
	set { Set(ref _someText, value); }
}

The Set method sets the property’s value and raises the PropertyChanged event if necessary. This allows the reduction of the setter method’s body to only one line, however the full property declaration still needs several lines of code since it is still necessary to define getter and setter method as well as the backing field manually.

MVVMbasics 2.2 finally supports the use of auto-properties as bindable properties. Properties defined in Models or Viewmodels that are marked with the MVVMbasics.Attributes.MvvmBindable attribute automatically raise the PropertyChanged event when their value is updated:

[MvvmBindable]
public string SomeText { get; set; }

While this significantly reduces code lines for bindable property declaration, it can still be simplified by using the [MvvmBindableProperties] attribute: In Viewmodels that contain many bindable properties, this attribute can be assigned to the Viewmodel class – in this case, all public properties defined within the Viewmodel class are treated as bindable, meaning that they will raise the PropertyChanged event when changed. To exclude single properties from this general rule, they can be marked with the [MvvmBindableIgnore] attribute:

[MvvmBindableProperties]
public class MyViewmodel : BaseViewmodel
{
	public string SomeText { get; set; }  // raises the PropertyChanged event
	
	public int SomeNumber { get; set; }  // raises the PropertyChanged event
	
	[MvvmBindableIgnore]
	public bool SomeInternalProperty { get; set; }  // does not raise the PropertyChanged event!
}

Please note that the [MvvmBindable...] attributes only work on properties that are defined within classes that inherit (directly or not) from the BaseModel or BaseViewmodel class! The reason for this limitation is that the automatic event raising mechanism that is responsible for all this magic relies on the NotifyPropertyChanged method which is defined by the BaseModel and BaseViewmodel classes.

Sometimes, bindable properties need to raise the PropertyChanged event more than once: The most typical case is a property that specifies only a getter method which returns a combination of several other properties:

public string FirstName { get; set; }

public string LastName { get; set; }

public string FullName
{
	get { return String.Format("{0} {1}", FirstName, LastName); }
}

The [MvvmBindable...] attributes even work in this case, since the underlying automatic event raising mechanism work in two iterations: First, all properties that don’t define a setter method are analyzed, and all the properties that are referenced in their getter methods are stored. In the second iteration, all properties that define setter methods are adapted, in order to raise their own PropertyChanged as well as all their dependent properties’ events! Note that for this to work, also the “read-only” properties (that only contain getter methods) need to be marked with the [MvvmBindable] attribute, or the whole class needs to be marked with the [MvvmBindableProperties] attribute.

public class MyViewmodel : BaseViewmodel
{
	[MvvmBindable]
	public string FirstName { get; set; }  // raises the PropertyChanged event for properties FirstName and FullName

	[MvvmBindable]
	public string LastName { get; set; }  // raises the PropertyChanged event for properties LastName and FullName

	[MvvmBindable]
	public string FullName
	{
		get { return String.Format("{0} {1}", FirstName, LastName); }
	}
}

Of course, the conventional way of creating a backing field and calling the Set method inside the property’s setter method is still available. For complex property declarations where the automatic event raising mechanism fails, you can still rely on this approach. Also if you like to automatically call a certain action before or after the property update, the Set method might be the better solution. All of the options it offers is documented in the article New version 2.0 features #2: Simplified Data Binding with more flexibility.

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

Mar 18

[MVVMbasics] Targeting the Android and iOS platforms via Xamarin.Forms #2

Notice

All blog posts with a title that starts with [MVVMbasics] are related to the MVVMbasics framework. If you are only interested in my regular blog posts and not in MVVMbasics, ignore these. To list all articles covering MVVMbasics, go to http://mvvmbasics.mobilemotion.eu/documentation!

Project setup in Xamarin Studio:

When using the Xamarin Starter, Indie, or Academic license (or when using Mac OS), it is not possible to use Visual Studio as IDE, instead you might be forced to use Xamarin Studio for development. In this case, you can basically follow the same steps for setting up the project structure as if you were using Visual Studio, however there are a few items that need special attention:

  • Xamarin Studio doesn’t support the creation of Windows Phone applications. Therefore, the solution won’t contain a WinPhone project.

    I’d strongly recommend to add an additional Portable Class Library (PCL) to the automatically created projects that contains all Data Models, Viewmodels, and Service interfaces. In this case, you can create a separate Windows Phone solution in Visual Studio, add a reference to this PCL project, and create a WinPhone application out of the given Models and Viewmodels. In this case, you are not even restricted to using Windows Phone Silverlight (as you would be if using pure Xamarin.Forms), but the Phone project can also be a WinRT App that also runs on Windows 8 devices. (Of course, in this case the effort is a little higher than with Xamarin.Forms-only project, since the Views and Service implementations must be created individually for the Silverlight / WinRT part.)

  • The use of an additional PCL project that contains Models and Viewmodels, as suggested above, also ensures that the App can be ported to other platforms (e.g., WPF) at a later point in time. Alternatively, this PCL can also be created in Visual Studio as part of a WPF solution, and referenced from Xamarin Studio at a later point in time to create a Xamarin.Forms App that provides the same functionality as the WPF application.

  • The MVVMbasics NuGet package contains PowerShell scripts that need to run during package installation in order to add all necessary assembly references. When adding NuGet references through the visual NuGet Package Manager using Xamarin Studio on Mac OS, these PowerShell scripts are not executed. Instead, I recommend to install the NuGet PowerShell Console and use this add-in for adding the MVVMbasics reference.

    To do so, choose the Package Console Extension item from the View -> Pads menu after the NuGet PowerShell Console add-in has been installed, and type Install-Package MvvmBasics to add the MVVMbasics framework to the current project!

Implementing Views:

When creating pages, make sure to add a new item of type Forms Xaml Page as this will allow you to specify the page’s content in XAML code. All pages that shall be automatically assigned their Viewmodel by the MVVMbasics NavigatorService must be derived from the MVVMbasics.Views.BaseView base class. All pages generated by Visual Studio or Xamarin Studio usually are derived from the Xamarin ContentPage class, so you must adapt each page’s base class in both C# and XAML code.When referencing the MVVMbasics.Views namespace in XAML, note that it is defined within the MVVMbasicsXF assembly:

xmlns:views="clr-namespace:MVVMbasics.Views;assembly=MVVMbasicsXF"

The MVVMbasics.Views.BaseView class itself inherits from Xamarin.Forms.ContentPage, so you may design the page’s content as if it was a default ContentPage. The creation of other Xamarin page types (e.g., TabbedPage, CarouselPage, …) is not supported by MVVMbasics at the moment, however the MVVMbasics framework provides an alternative for using NavigationPages as shown in the following section Implementing the global App class.

In addition, the MvvmNavigationTarget attribute should be added to each View class, as this enables the automatic assignment of Viewmodels to View. In summary, a sample MVVMbasics page could be implemented as follows:

<?xml version="1.0" encoding="utf-8" ?>
<views:BaseView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:views="clr-namespace:MVVMbasics.Views;assembly=MVVMbasicsXF"
             x:Class="MyApp.Views.MyPage">
	<Label Text="{Binding DisplayText}"/>
</views:BaseView>
using Core.Viewmodels;
using MVVMbasics.Attributes;
using MVVMbasics.Views;

namespace MyApp.Views
{
	[MvvmNavigationTarget(typeof(MyViewmodel))]
	public partial class MyPage : BaseView
	{
		public MyPage()
		{
			InitializeComponent();
		}
	}
}

Implementing the global App class:

Each Xamarin.Forms application contains one global App.cs class file, which is instantiated and started during App startup by the various platform-specific projects. When using MVVMbasics, this App class must be derived from the MVVMbasics.BaseApplication base class. This one itself inherits from Xamarin.Forms.Application, which means that inside your App class you can do whatever you are used to from any other Xamarin.Forms applications, e.g. override the OnSleep() and OnResume() methods.

Within the App class’ constructor, you may register all desired MVVM services to the global ServiceLocator, as is the case in all MVVMbasics applications on other platforms. The only major difference to a typical Xamarin.Forms App class is that instead of assigning the App’s startup page to the MainPage property, you call the SetStartupPage method. This method expects the App’s startup page either as type parameter (in which case it is automatically instantiated) or as parameter (if it has been instantiated already):

SetStartupPage<MyPage>();
SetStartupPage(new MyPage());

Internally, the SetStartupPage method instantiates the provided page if necessary and assigns it to the App’s MainPage property. In addition, it checks whether the MVVMbasics NavigatorService has been registered and if it contains more than one View / Viewmodel pair – in this case it can be assumed that the App shall support page navigation, and the startup page is automatically wrapped within a Xamarin.Forms.NavigationPage. This means that there is no need to manually create an instance of NavigationPage!

In summary, the full implementation of an MVVMbasics App class for Xamarin.Forms could look as follows:

public class App : BaseApplication
{
	public App()
	{
		// Services used by the application
		var navigationService = new NavigatorService();
		navigationService.RegisterAll("MyApp.Views.*");
		ServiceLocator.Register(navigationService);
		ServiceLocator.Register<MessageboxService>();

		// The root page of the application
		SetStartupPage<MyPage>();
	}
}

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

Older posts «