In the
last article we looked at the basics of sharing code. This article can be supplemented with the possibility of sharing between W8.1 and WP8.1 demonstrated at the Build conference. This approach is very well described
here , so now we will not dwell on Universal Apps in detail.

In general, Microsoft is pleased with the steps to unify the code for both platforms, but still we still have a legacy in the form of Windows Phone 7. In addition, you may have to fumble the code on the desktop, Android, etc.
In this article, we will look at one of the most commonly used practical solutions for Sharing code.
Preparing for the sharing code
The following “tools” significantly simplify the writing of cross-platform applications, but since there are quite a few articles / books written about these approaches and patterns, we will not dwell on these points in detail.
The MVVM pattern has actually become almost an obligatory quality standard when developing applications for WP, W8, WPF, Silverlight; and in spite of the fact that it is not necessary to use it at all, yet this approach often saves a lot of time and code when creating complex applications.
')
One of the main misconceptions that I often come across is the opinion that you can just take and use MVVM, and the quality will come by itself. Or that this pattern will replace, for example, the classic three-link. In fact, MVVM does not completely deny the selection of application logic and data storage logic. In fact, everything is exactly the opposite: if you write all the application logic and data storage logic directly in the VM, then, in fact, we get the same Code-Behind, only in the VM, and the application becomes clogged very quickly and becomes difficult to maintain.
The most popular framework for WP and WinRT is, perhaps,
MVVM Light , but in my applications, I most often prefer to use my own lightweight self-written framework.
Inversion Of Control - if we can do without MVVM when writing cross-platform applications, then, perhaps, inversion of control is a must-have tool. Even in the case of applications for a single platform, IoC greatly simplifies the development of flexible, extensible and, accordingly, convenient for further maintenance of applications.
The main idea of ​​using IoC in developing cross-platform applications is a generalization of features for each platform using an interface and a specific implementation for each platform.
There are many ready-made IoC containers, but in my applications I use either a self-service Service Locator (which many people believe is anti-pattern), and in some projects I use the lightweight framework SimpleIoC (which, by the way, comes bundled with MVVM Light).
Consider an example of the preservation of the text from the
previous article , where we shared the features of preserving text with the help of the directive "#if WP7". It was possible to implement this example a little differently. For example, if this method is in a certain class with DataLayer, then our code could look like this:
In a project with a common code (for example, the Portable Library, which we will discuss below), we can identify a common interface for saving text:
public interface IDataLayer { Task SaveText(string text); }
We can use this interface in our logic layer. For example:
public class LogicLayer : ILogicLayer { private readonly IDataLayer dataLayer; public LogicLayer(IDataLayer dataLayer) { this.dataLayer = dataLayer; } public void SomeAction() { dataLayer.SaveText("myText"); } }
As you can see, the layer of logic does not know anything about how and where the text will be saved.
Accordingly, the text saving implementation for WP7 / WP8 might look like this:
public class DataLayerWP : IDataLayer { public async Task SaveText(string text) { using (var local = IsolatedStorageFile.GetUserStoreForApplication()) { using (var stream = local.CreateFile("DataFile.txt")) { using (var streamWriter = new StreamWriter(stream)) { streamWriter.Write(text); } } } } }
And, accordingly, for WP8 / W8 there may be the following code:
public class DataLayerWinRT : IDataLayer { public async Task SaveText(string text) { var fileBytes = Encoding.UTF8.GetBytes(text); var local = ApplicationData.Current.LocalFolder; var file = await local.CreateFileAsync("DataFile.txt", CreationCollisionOption.ReplaceExisting); using (var s = await file.OpenStreamForWriteAsync()) { s.Write(fileBytes, 0, fileBytes.Length); } } }
Of course, the names of the classes may coincide, since these classes are specific implementations and each of them will be in a project with a specific platform.
All that is now left is to register in each of the projects a specific implementation. If we take the example of SimpleIoC, then the registration might look like this (in our case for WP7):
SimpleIoc.Default.Register<IDataLayer,DataLayerWP>();
and in a project with WP8:
SimpleIoc.Default.Register<IDataLayer,DataLayerWinRT>();
Thus, we can share almost everything: page-by-page navigation, receiving data from sensors, opening an html page on a client, etc. etc.
For those who plan to actively use Xamarin, I can recommend
Xamarin mobile api , which is a set of specific implementations for solving a wide variety of tasks, such as storing data, getting a user's location, taking a picture from a camera, etc.
Portable Library. Starting from VS2012, we were able to use a new type of project, the Portable Library (PL). Strictly speaking, we got this opportunity also with VS2010, but as a separately installed extension. PL allows you to create applications with common code, and the main “trick” of this type of project is that PL automatically uses only those language features that are common to selected types of projects. This imposes its own limitations and peculiarities of using this tool. For example, you cannot use XAML in PL. However, for applications with complex logic, PL will bring tremendous time and effort savings.
Perhaps it’s worth mentioning separately the problem of using the CallerMember attribute, which is not supported by PL and which allows BaseViewModel to highlight the following common for all VM method:
public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName]string propertyName=null) { var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
What allows you to write instead:
public string Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } }
Alternative entry without specifying a field:
public string Name { get { return name; } set { name = value; OnPropertyChanged(); } }
That, respectively, significantly simplifies the maintenance of the application.
To add support for this attribute in PL, it is enough to manually declare it:
CallerMemberNameAttribute.cs
namespace System.Runtime.CompilerServices {
Setup and connection
There were a lot of examples of using MVVM and IoC even on Habré, here are some links instead of inflating the article:
Xamarin + PCL + MVVM — How to make it easier to write mobile applications for different platforms. - excellent article on the use of Xamarin and MVVM
Using the MVVM pattern when creating applications for Windows Phone . More details on MVVM and usage in WP can be found in this article.
Windows Phone + Caliburn.micro + Autofac . Another article about how to configure and use the popular MVVM framework Caliburn.Micro, and the equally popular IOC container Autofac when building web and desktop applications.
Of course, this is not a definitive list, and in the vastness of Habr and the Internet you can find equally interesting articles on this topic.
Next, we can consider the features of writing cross-platform applications using these tools.
Shared VM
The first obvious solution that comes to mind is to use a shared VM for each type of project. In fairly simple projects, we can do just that.
For example, a VM with a calculator that adds two numbers might look like this:
public class MainViewModel : BaseViewModel { private int valueA; public int ValueA { get { return valueA; } set { valueA = value; OnPropertyChanged(); } } private int valueB; public int ValueB { get { return valueB; } set { valueB = value; OnPropertyChanged(); } } public ICommand CalculateCommand { get { return new ActionCommand(CalculateResult); } } private void CalculateResult() { Result = ValueA + ValueB; } private int result; public int Result { get { return result; } private set { result = value; OnPropertyChanged(); } } }
And we can use this VM without changes in each of the platforms.
In our simple case, we can have the same simple XAML code for both platforms for this VM.
<StackPanel> <TextBlock Text="A:"></TextBlock> <TextBox Text="{Binding ValueA, Mode=TwoWay}"></TextBox> <TextBlock Text="B:"></TextBlock> <TextBox Text="{Binding ValueB, Mode=TwoWay}"></TextBox> <Button Command="{Binding CalculateCommand}" Content=""></Button> <TextBlock Text="{Binding Result}"></TextBlock> </StackPanel>
VM detailing for each platform
Often it is necessary to take into account the features of each platform. For example, for Win8, where we have a big screen, the task arose of outputting the result in words as well, but for WP it was decided to output only the result less than 100 in words.
The first and usually the wrong decision, which you immediately want to use, may look like this:
private int result; public int Result { get { return result; } private set { result = value; OnPropertyChanged(); OnPropertyChanged("ResultTextWP"); OnPropertyChanged("ResultTextWinRT"); } } public string ResultTextWP { get { if (result < 100) return Result.ToString(); return NumberUtil.ToText(Result); } } public string ResultTextWinRT { get { return NumberUtil.ToText(Result); } }
Where we can use each of the fields for a specific platform. Instead, we can use inheritance as a VM customization, i.e. in PL in the MainViewModel class, we can declare one virtual field ResultText:
private int result; public int Result { get { return result; } private set { result = value; OnPropertyChanged(); OnPropertyChanged("ResultText"); } } public virtual string ResultText { get { throw new NotImplementedException(); } }
Where we can replace the throw new NotImplementedException (), for example, with the return NumberUtil.ToText (Result), if we don’t want to force each heir to explicitly override this field for use
Now we can inherit the MainViewModel in each project and override its property:
public class MainViewModelWinRT : MainViewModel { public override string ResultText { get { return NumberUtil.ToText(Result); } } }
And for WP:
public class MainViewModelWP : MainViewModel { public override string ResultText { get { if (result < 100) return Result.ToString(); return NumberUtil.ToText(Result); } } }
Of course, in the IoC container we have to register instead of the general MainViewModel a specific implementation for each platform.
If we need ONLY one property of a VM on one platform, - by itself, there is no need to define it in the base class, then we can specify this property only for a specific platform. For example, suddenly it took to show the date in the UI only for W8:
public class MainViewModelWinRT : MainViewModel { public override string ResultText { get { return NumberUtil.ToText(Result); } } public string Date { get { return DateTime.Now.ToString("yy-mm-dd"); } } }
Summary
At first glance, compared with the previous article, we get a huge amount of unnecessary headache with MVVM, IoC, etc., while we can simply link files and share features with #if directives.
Unfortunately, the calculator example does not show the advantages, but only the receipt of a significantly larger amount of code for such a simple application.
In practice, you can get much more advantages when building medium and large applications with complex logic, due to the fact that our code occupies a significantly larger part of the "service" code. In addition, the application code becomes less connected and application maintenance is greatly facilitated.
Unfortunately, there are still many items “overboard” that I did not include in this article, so as not to clutter up the main points of sharing code. If you tell in the comments which problems / topics / solutions you are most interested in, then I can add this article with one more article.