InternetException

About coding and whatnot.

Faster than Reflection: Delegates. Part 1.

clock August 5, 2016 05:58 by author n.podbielski

Why not Reflection?

If you working with .NET and C# (probably other languages too) at some point you will have to write more advanced and generic code, that i.e. reads all properties of a type, searches for attributes etc. Or maybe you want to call some private method or property? It is not that rare to use some internal mechanism of framework or library. Usually first solution involves Reflection. But thing is: Reflection is slow. Of course it is sufficient if you need to use Reflection rarely and it is not main code of application. If it is used more often at some point you will probably try to optimize it. And now is the fun part: how to do it?

Main problem with Reflection is that it is slow. It is much slower then calling members of types directly. If you do this via Reflection everything slows down. If you need to create instance of a type you probably will use Activator.CreateInstance method. It is a little quicker than reflection but is also slower then calling constructor directly. For properties, methods and constructors it is possible to use expressions (Expression type static methods), but expressions and compiling them is even slower then reflection.

When I was looking for a fast way to create types instances in this article, I have found this question on Stack Overflow. According to it, fastest way to create instance is to use delegate created by compiling expression. This made me thinking about general mechanism for acquiring instances and members of types via delegates. After all it is point of interest for me lately since I work on Xamarin applications with PCL projects that have portable .NET framework with a little less features then full version. Fast mechanism for returning not available members of types would be really nice and would make PCL code bigger in the expense of platform code (which is good thing). And since you can create delegates from Reflection, Expression or lambdas and there are just a little overhead (since you have call one more method), mechanism for creating delegates for acquiring all members of a type (public and private) is a good idea. And this is main topic of this article.

 

 Following code is written in Visual Studio 2015 with use of .NET 4.5, but it is possible to port most of the code to older versions.

 

Delegates for everything

Type can have following types of members:

  1. Static
    1. Properties
    2. Methods
    3. Fields
    4. Events
    5. Constant fields
  2. Instance
    1. Properties
    2. Methods
    3. Fields
    4. Constructors
    5. Events
    6. Indexers

We can create delegates for retrieving all of this members. Frankly not all of those delegates makes sense. Delegates for constants for example. Those fields do not change, so there is no point of retrieving them more than once. You can easily retrieve those values using Reflection and save it somewhere for future use.

They only thing this article will not cover is static events. Did you ever use one? Seriously I cannot think of a case when this is absolutely needed. Much more common would to create ordinary event and invoke it from singleton instance. Good thing is that it is pretty easy to create delegate for static event with just small change for instance event.

 

 

Static Properties

Properties and methods are most common members. Methods are much more complex so it is better to start with properties.

I think most common case for reflection is retrieving set or single properties from instance or type. It may be for dynamic table views, serialization, deserialization, saving to file etc. Static properties are little less complex so we will start with them instead of instance ones.

 

Get Accessor

We should create new project. It will called Delegates - console application for easy tests.

First thing to do is to create class for tests. We can named it TestClass.

public class TestClass
{
    public static string StaticPublicProperty { get; set; } = "StaticPublicProperty";

    internal static string StaticInternalProperty { get; set; } = "StaticInternalProperty";

    protected static string StaticProtectedProperty { get; set; } = "StaticProtectedProperty";

    private static string StaticPrivateProperty { get; set; } = "StaticPrivateProperty";
}

Second thing is to create class for delegates creation - DelegatesFactory. How we can retrieve static property? Best way is to retrieve it is via Type instance that represent info about our source type. We can make it extension method for Type type. This way it will be more convenient. We need to know about property we want, so parameter with property name and type parameter with its return type is also good idea.

Type.StaticPropertyGet<string>("StaticPublicProperty");

Another way is to set source type and property type as type parameters. This way we will have static method instead of an extension method. Also it would be less usable since we often do not have type name in compile time (because it is not public or not available before runtime).

DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty");

With this assumption we can write console application as test for DelegateFactory.

class ConsoleApplication
{
    private static readonly Type Type = typeof(TestClass);

    static void Main(string[] args)
    {
        var sp = DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty");
        var sp1 = Type.StaticPropertyGet<string>("StaticPublicProperty");
    }
}

Now we can write stub of both methods.

public static class DelegateFactory
{
    public static Func<TProperty> StaticPropertyGet<TSource, TProperty>(string propertyName)
    {
        return null;
    }

    public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName)
    {
        return null;
    }
}

Since second method (extension one) is more general we will implement it first. To create delegate we need PropertyInfo for requested property. Yes, we need reflection for that. Thing is, that much of delegates requires reflection, but it is just one time, for creation of delegate. After that we can use this delegate with just small overhead, since program have to execute two nested methods instead of one.

Most important part in retrieving property via reflection is that we have access to PropertyInfo.GetMethod that way and GetMethod is MethodInfo type which have CreateDelegate member. Simplest code for creating delegate for retrieving static public property looks like this.

public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = source.GetProperty(propertyName, BindingFlags.Static);
    return (Func<TProperty>)propertyInfo.GetMethod.CreateDelegate(typeof(Func<TProperty>));
}

Looks really simple, which is why this is first example. CreateDelegate method requires type of delegate to create. For property retrieving it is Func with just one parameter, which is type of property, so created delegate will not have any parameters. Just simple parameterless method that returns value of static property.

Next thing is to handle non-public (internal, protected and private) properties. We can do it by changing set BindingFlags in GetProperty method. For private and protected members we just need one more BindingFlags.NonPublic. For internals this is little more complicated (and in my opinion more confusing) and requires both flags Public and NonPublic. Easy way to understand that is to think of internals as kind-of public, but not completely Smile.

public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = source.GetProperty(propertyName, BindingFlags.Static);
    if (propertyInfo == null)
    {
        propertyInfo = source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic);
    }
    if (propertyInfo == null)
    {
        propertyInfo = source.GetProperty(propertyName,
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
    }
    return (Func<TProperty>)propertyInfo.GetMethod.CreateDelegate(typeof(Func<TProperty>));
}

 Above code is pretty easy to understand. If property is not public it tries to find if as private or protected. If this also not the case it tries internal. Now we can write method body for overload with type parameters.

public static Func<TProperty> StaticPropertyGet<TSource, TProperty>(string propertyName)
{
    return typeof(TSource).StaticPropertyGet<TProperty>(propertyName);
}

Now we can test both methods. We should create delegates for all static properties of TestClass in console application and use them to retrieve properties values, to test if everything works.

class ConsoleApplication
{
    private static readonly Type Type = typeof(TestClass);

    static void Main(string[] args)
    {
        var sp = DelegateFactory.StaticPropertyGet<TestClass, string>("StaticPublicProperty");
        var sp1 = Type.StaticPropertyGet("StaticPublicProperty");
        var sp2 = Type.StaticPropertyGet("StaticInternalProperty");
        var sp3 = Type.StaticPropertyGet("StaticProtectedProperty");
        var sp4 = Type.StaticPropertyGet("StaticPrivateProperty");

        Console.WriteLine("Static public property value: {0}",sp1());
        Console.WriteLine("Static internal property value: {0}", sp2());
        Console.WriteLine("Static protected property value: {0}", sp3());
        Console.WriteLine("Static private property value: {0}", sp4());
    }
}

After running this example we will see result in console.

Static public property value: StaticPublicProperty
Static internal property value: StaticInternalProperty
Static protected property value: StaticProtectedProperty
Static private property value: StaticPrivateProperty

Static properties works just fine Smile.

We can check performance of those delegates a little. First we need to add two new fields to console application type.

private static Stopwatch _stopWatch;
private static double _delay = 1e8;

Second is to write loops for tests.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = TestClass.StaticPublicProperty;
}
_stopWatch.Stop();
Console.WriteLine("Static Public property: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = sp1();
}
_stopWatch.Stop();
Console.WriteLine("Static Public property retriever: {0}", _stopWatch.ElapsedMilliseconds);

First for loop retrieves static property in ordinary way. Second one uses delegates. After running changed code we can find out that difference is not that big, really.

Static Public property: 404
Static Public property retriever: 472

Retriving property value one hundred million times takes around 400ms and using delegate for that is less than 20% slower. Doing more tests you can check that real performance overhead varies between 15-35%. Of course it is just simple string. With more complex properties that are calculating its return value first, difference would be smaller. Just for a sake of an experiment we can add one more loop with reflection.

_stopWatch = new Stopwatch();
var pi0 = Type.GetProperty("StaticPublicProperty", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public).GetMethod;
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = pi0.Invoke(null, null);
}
_stopWatch.Stop();
Console.WriteLine("Static Public property via reflection: {0}", _stopWatch.ElapsedMilliseconds);

After running changed code test results should be similar to this:

Static Public property: 423
Static Public property retriever: 535
Static Public property via reflection: 13261

As you can see reflection is many times slower. Access to static property is less than half of a second, and doing the same via reflection takes around 13 seconds!

Even by using reflection only one time to create delegate is much more efficient than using it all the time to access that property.

To have complete look on things, let us do one more test. If you ever used Expression type and its method you know that it can be used for similar things as reflection. Certainly it is possible to create delegate for static property this way.

public static Func<TProperty> StaticPropertyGet2<TProperty>(this Type source, string propertyName)
{
    var te = Expression.Lambda(Expression.Property(null, source, propertyName));
    return (Func<TProperty>)te.Compile();
}

This code is much simpler than one with reflection. It creates lambda function that could look like this for TestClass.StaticPublicProperty.

() => TestClass.StaticPublicProperty

Now we can test performance of creation of delegates by using reflection and expression.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < 10000; i++)
{
    Type.StaticPropertyGet<string>("StaticPublicProperty");
}
_stopWatch.Stop();
Console.WriteLine("Static public field creator via reflection: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < 10000; i++)
{
    Type.StaticPropertyGet2<string>("StaticPublicProperty");
}
_stopWatch.Stop();
Console.WriteLine("Static public field creator via expression: {0}", _stopWatch.ElapsedMilliseconds);

After running this example we will get results for both loops.

Static public field creator via reflection: 23
Static public field creator via expression: 543

As we can see expression is much, much more slower than reflection. I do not investigate this further, but I think that it have something to do with compiling expression to delegate. Unfortunately it is not possible to creates delegates for all members just by using reflection so it is necessity to use expressions for some of them, but we will try to not to if this is possible.

There is still the case when we do not know type of property. What to do then? Since both of StaticPropertyGet require an actual property type we cannot use them. In that case we need an overload without this type, but what to use instead? What type should delegate return? There is only one option: object. All types in .NET can be casted to object.

Func<object> spg = Type.StaticPropertyGet("StaticPublicProperty");

Returned delegate supposed to be a method that do not takes parameters and returns value of type object. How to implement this new StaticPropertyGet? We need code for retrieving PropertyInfo for needed static property. Best thing is to create new method in DelegateFactory using code from StaticPropertyGet<TProperty> extension method.

private static PropertyInfo GetStaticPropertyInfo(Type source, string propertyName)
{
    var propertyInfo = (source.GetProperty(propertyName, BindingFlags.Static) ??
                               source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic)) ??
                               source.GetProperty(propertyName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
    return propertyInfo;
}

Main problem is that sometimes we want to retrieve or set property of a value that is of unknown type at compile time. For that we need method that creates delegates that retrieves object type value instead of property type value. We can do it by using expressions since we cannot use just CreateDelegate method (it requires compatible delegate signature). Core of this mechanism is Expression.Lambda method, that returns LambdaExpression type object, and LambdaExpression.Compile method that returns Delegate type. With those two we can create pretty much any code that involves accessing of types members.

public static Func<object> StaticPropertyGet(this Type source, string propertyName)
{
    var propertyInfo = GetStaticPropertyInfo(source, propertyName);
    return (Func<object>)Expression.Lambda(Expression.Call(propertyInfo.GetMethod)).Compile();
}

Expression.Lambda creates lambda with correct method signature. Expression.Call is used to mark in body of a lambda that call to a property getter should be made - after all property getter is really get_{property name} method.

We can test if this works correctly with following code.

var spg = Type.StaticPropertyGet("StaticPublicProperty");
Console.WriteLine(spg());

Above lines executed will write value of 'StaticPublicProperty', which is value of property with this name.

 

Set accessors

Now when we have a way to get values of static properties, we can write methods for retrieving delegates for set accessors. Mechanism is very similar to get accessors, but instead of Func<TProperty> Action<TProperty> is used. Of course PropertyInfo.SetMethod creates delegate instead of GetMethod.

public static Action<TProperty> StaticPropertySet<TSource, TProperty>(string propertyName)
{
    return typeof(TSource).StaticPropertySet<TProperty>(propertyName);
}
public static Action StaticPropertySet(this Type source, string propertyName)
{
    var propertyInfo = GetStaticPropertyInfo(source, propertyName);
    return (Action)propertyInfo.SetMethod.CreateDelegate(typeof(Action));
}

As before, for get accessors, there are two methods that uses only reflection: one extension method and one with type parameter as source type.

For overload that do not require property type to be known in advance we have to use expressions as before with StaticPropertyGet.

public static Action<object> StaticPropertySet(this Type source, string propertyName)
{
    var propertyInfo = GetStaticPropertyInfo(source, propertyName);
    var valueParam = Expression.Parameter(typeof(object));
    var convertedValue = Expression.Convert(valueParam, propertyInfo.PropertyType);
    return (Action<object>)Expression.Lambda(Expression.Call(propertyInfo.SetMethod, convertedValue), valueParam).Compile();
}

This expression is a bit more complicated. Since it should return method that returns void and have one parameter, we need to add this parameter to Lambda call - hence valueParam variable. If you are wondering why it cannot be embedded into Expression.Lambda, the same parameter expression has to be passed to Convert and Lambda methods - expression are compared by reference. Variable valueParam has to be converted to correct type before passing to property setter method. If not expression would be valid and would failed to create. After conversion, new expression can be safely passed as parameter to Call method. Not converted expression in valueParam variable is passed as parameter to Lambda call, to mark, that created lambda will have single parameter with type of object.

New methods are called exactly the same way.

var sps = DelegateFactory.StaticPropertySet<TestClass, string>("StaticPublicProperty");
var sps1 = Type.StaticPropertySet<string>("StaticPublicProperty");
var sps2 = Type.StaticPropertySet<string>("StaticInternalProperty");
var sps3 = Type.StaticPropertySet<string>("StaticProtectedProperty");
var sps4 = Type.StaticPropertySet<string>("StaticPrivateProperty");
var spg5 = Type.StaticPropertyGet("StaticPublicProperty");

In console application we can create test loops like below.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    TestInstance.PublicProperty = "ordinal way";
}
_stopWatch.Stop();
Console.WriteLine("Public set property: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    sps1("test");
}
_stopWatch.Stop();
Console.WriteLine("Public set property retriever: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    sps2("test");
}
_stopWatch.Stop();
Console.WriteLine("Internal set property retriever: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    sps3("test");
}
_stopWatch.Stop();
Console.WriteLine("Protected set property retriever: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    sps4("test");
}
_stopWatch.Stop();
Console.WriteLine("Private set property retriever: {0}", _stopWatch.ElapsedMilliseconds);

Console.WriteLine("Static public property value is {0}", sp1());
Console.WriteLine("Static internal property value is {0}", sp2());
Console.WriteLine("Static protected property value is {0}", sp3());
Console.WriteLine("Static private property value is {0}", sp4());

After running above code we will see result with values similar to the ones below.

Public set property: 483
Public set property retriever: 542
Internal set property retriever: 496
Protected set property retriever: 497
Private set property retriever: 542
Static public property value is test
Static internal property value is test
Static protected property value is test
Static private property value is test

As you can see accessing setters this way is just a little slower, similarly to accessing getters by delegates.

 

Improvements

What can be done better? Certainly code above is not very safe since there are at least two points when it can throw null reference exception. First, when property was not found and propertyInfo variable is null and second when get or set accessor is not available (not every property have both, right?). Both cases are easy to fix.

We should write properties to test those cases first.

public static string StaticOnlyGetProperty => _staticOnlyGetOrSetPropertyValue;

public static string StaticOnlySetProperty
{
    set { _staticOnlyGetOrSetPropertyValue = value; }
}
private static string _staticOnlyGetOrSetPropertyValue = "StaticOnlyGetOrSetPropertyValue";

Now we can fix both methods.

public static Func<TProperty> StaticPropertyGet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = GetStaticPropertyInfo(source, propertyName);
    return (Func<TProperty>)propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TProperty>));
}

public static Action<TProperty> StaticPropertySet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = GetStaticPropertyInfo(source, propertyName);
    return (Action<TProperty>)propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TProperty>));
}

As you can see, simple null check in both methods is enough, but we should test it anyway. Following calls in console application will suffice.

 

All calls returns null, which is good. We wanted null instead of error.

 

Properties

With static properties covered we can now switch focus to instance properties. Delegates for those are different only by one parameter. Since instance properties need instance to get values of those properties from, delegates will need parameters with those instance. So instead of Func<TProperty> will use Func<TSource,TProperty> delegates. Rest of the code is almost the same. As for static properties we have three methods for each accessor. First, static method with source type parameter and property type parameter, extension method with type parameter the same as property type and method without type parameters. But firs we need method similar to GetStaticPropertyInfo.

private static PropertyInfo GetPropertyInfo(Type source, string propertyName)
{
    var propertyInfo = source.GetProperty(propertyName) ??
                               source.GetProperty(propertyName, BindingFlags.NonPublic) ??
                               source.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    return propertyInfo;
}

As you can see it is very similar too

We will start with first type of method for each accessor.

public static Func<TSource, TProperty> PropertyGet<TSource, TProperty>(string propertyName)
{
    var source = typeof(TSource);
    var propertyInfo = GetPropertyInfo(source, propertyName);
    return (Func<TSource, TProperty>)propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TProperty>));
}

public static Action<TSource, TProperty> PropertySet<TSource, TProperty>(string propertyName)
{
    var source = typeof(TSource);
    var propertyInfo = GetPropertyInfo(source, propertyName);
    return (Action<TSource, TProperty>)propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TProperty>));
}

As you can see there is null propagation operator (?.) used for null check of property info and needed accessor method.

Ok. To test it we need to create instance properties in TestClass in console application.

public class TestClass
{
    public TestClass()
    {
        PublicProperty = "PublicProperty";
        InternalProperty = "InternalProperty";
        ProtectedProperty = "ProtectedProperty";
        PrivateProperty = "PrivateProperty";
    }

    public string PublicProperty { get; set; }

    internal string InternalProperty { get; set; }

    protected string ProtectedProperty { get; set; }

    private string PrivateProperty { get; set; }

}

 And static readonly instance of TestClass type in console application to use in all test that involves instance.

private static readonly TestClass TestInstance = new TestClass();

Test code looks like below.

var ps1 = DelegateFactory.PropertySet<TestClass, string>("PublicProperty");
var ps2 = DelegateFactory.PropertySet<TestClass, string>("InternalProperty");
var ps3 = DelegateFactory.PropertySet<TestClass, string>("ProtectedProperty");
var ps4 = DelegateFactory.PropertySet<TestClass, string>("PrivateProperty");
            
Console.WriteLine("Public property value is {0}", pg1(TestInstance));
Console.WriteLine("Internal property value is {0}", pg2(TestInstance));
Console.WriteLine("Protected property value is {0}", pg3(TestInstance));
Console.WriteLine("Private property value is {0}", pg4(TestInstance));

ps1(TestInstance, "test");
ps2(TestInstance, "test");
ps3(TestInstance, "test");
ps4(TestInstance, "test");

Console.WriteLine("Public property value is {0}", pg1(TestInstance));
Console.WriteLine("Internal property value is {0}", pg2(TestInstance));
Console.WriteLine("Protected property value is {0}", pg3(TestInstance));
Console.WriteLine("Private property value is {0}", pg4(TestInstance));

After running this test code we will see console output like this.

Public property value is PublicProperty
Internal property value is InternalProperty
Protected property value is ProtectedProperty
Private property value is PrivateProperty
Public property value is test
Internal property value is test
Protected property value is test
Private property value is test

First four lines are values returned by get delegates and are consistent with default values of TestClass instance. Four next lines are after calls to set delegates and have new 'test' values. Good. Everything works!

Improvements

Main problem with methods like above for retrieving and setting instance properties is that those require type know at compile time, which is sometimes impossible since some types are private. But if you do not have type you cannot create delegate that have this type instance as parameter. The only solution for this is to create method that takes object and hopes that it is correct instance of correct type Smile Problem with that solution is that you cannot pass parameter of type of object to property getter as source instance. You first have to cast it to appropriate type. So instead of just one proxy method you have cast to appropriate type too inside this proxy method, that retrieves or sets property. This is the point when we have to use expressions as in static properties.

Second problem is the same as with static properties. You do not necessarily have to know property type at compilation time. It may be either unavailable or private.

First of all our new overloads would look like this:

Type.PropertyGet<string>("PublicProperty");
Type.PropertyGet("PublicProperty");

Pretty simple. In first case we know type of property and in second one - we do not. First would return string and second one the same string but as object type.

We need two new overloads for PropertyGet method then. Let us start with the one with known property type since it will be only a bit, but a little simpler nevertheless.

With property info returned from GetPropertyInfo method, we can create appropriate delegate with Expression class. Code for accessing property getter by its instance as object looks like this.

var sourceObjectParam = Expression.Parameter(typeof(object));
var @delegate = (Func<object, object>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod), sourceObjectParam).Compile();

Mechanism is simple. First we need to create ParameterExpression with object type. It is necessary because it is referenced twice: in Expression.Lambda method as target lambda parameter and in Expression.Convert method as a target of a conversion. After that we create lambda expression (Expression.Lambda method) with body with method call (Expression.Call method) to property getter with parameter of the call from result of conversion (Expression.Convert method) of lambda object parameter. It may looks complicated (even more since Expression API is in my opinion a little chaotic; notice that Lambda method takes first body of a method, then parameters, but Call method first takes subject of a call and then method to call), but in reality it is not. After compilation expression will be roughly equivalent to lambda like below.

Func<object,string> @delegate = o => ((TestClass)o).PublicProperty;

Looks much clearer, right? Smile

With this knowledge we can create first method.

public static Func<object, TProperty> PropertyGet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = GetPropertyInfo(source, propertyName);
    var sourceObjectParam = Expression.Parameter(typeof(object));
    return (Func<object, TProperty>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod), sourceObjectParam).Compile();
}

The same way we can create second method, but with different declaration of return type. It is very important to mark, that even if method with return type object, written in C# can work without cast to object in return statement (because any type can be implicitly casted to object without extra casting), this do not work in expressions. As far as I can tell from my tests it works only for reference type. For value types such as int, DateTime etc. we need conversion to object. PropertyInfo class have IsClass property that allows to check if conversion is needed or not.

public static Func<object, object> PropertyGet(this Type source, string propertyName)
{
    var propertyInfo = GetPropertyInfo(source, propertyName);
    var sourceObjectParam = Expression.Parameter(typeof(object));
    Expression returnExpression = 
        Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod);
    if (!propertyInfo.PropertyType.IsClass)
    {
        returnExpression = Expression.Convert(returnExpression, typeof(object));
    }
    return (Func<object, object>)Expression.Lambda(returnExpression, sourceObjectParam).Compile();
}

As you can see second method is more generic and almost the same as first. Because of that we can safely rewrite first method like below.

public static Func<object, TProperty> PropertyGet<TProperty>(this Type source, string propertyName)
{
    return source.PropertyGet(propertyName) as Func<object, TProperty>;
}

After adding new property to TestClass, with int type, PublicPropertyInt and following lines to console application we can test if those methods works.

var pgo1 = Type.PropertyGet<string>("PublicProperty");
var pgo2 = Type.PropertyGet("PublicProperty");
var pgo3 = Type.PropertyGet("PublicPropertyInt");
Console.WriteLine("Public property by object and property type {0}", pgo1(TestInstance));
Console.WriteLine("Public property by objects {0}", pgo2(TestInstance));
Console.WriteLine("Public property by objects and with return value type {0}", pgo3(TestInstance));

It will result in following lines in console output.

Public property by object and property type PublicProperty
Public property by objects PublicProperty
Public property by objects and with return value type 0

Now we can write similar methods for setters. Core difference is that correct delegate will be Action instead of Func so instead of one parameter it will have to: instance and value to set. In method with known parameter type we only have change delegate but in the one without type parameters we have to convert instance and value. We will start with second one. First we have to create ExpressionParameter for new property value.

var propertyValueParam = Expression.Parameter(propertyInfo.PropertyType);

With this we can change returning value of lambda (if we changing similar PropertyGet method).

return (Action<object, object>)Expression.Lambda(Expression.Call(
Expression.Convert(sourceObjectParam, source),
propertyInfo.SetMethod,
Expression.Convert(propertyValueParam, propertyInfo.PropertyType)), sourceObjectParam, propertyValueParam).Compile();

As you can see its creates Action delegate with two parameters - sourceObjectParam and propertyValueParam - both are converted to correct type first. Above lines could be converted to following lambda.

Action<object, object> @delegate = (i, v)=>((TestClass)i).PublicProperty = (string)v;

PropertySet overload with known property type is more specific then above method, but in the same time it have knowledge of both types: value parameter and property type. This is why we can use its code for both overloads. Just if property type is correct there is no need for conversion.

Final version of both looks like this.

public static Action<object, TProperty> PropertySet<TProperty>(this Type source, string propertyName)
{
    var propertyInfo = GetPropertyInfo(source, propertyName);
    var sourceObjectParam = Expression.Parameter(typeof(object));
    ParameterExpression propertyValueParam;
    Expression valueExpression;
    if (propertyInfo.PropertyType == typeof(TProperty))
    {
        propertyValueParam = Expression.Parameter(propertyInfo.PropertyType);
        valueExpression = propertyValueParam;
    }
    else
    {
        propertyValueParam = Expression.Parameter(typeof(TProperty));
        valueExpression = Expression.Convert(propertyValueParam, propertyInfo.PropertyType);
    }
    return (Action<object, TProperty>)Expression.Lambda(Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, valueExpression), sourceObjectParam, propertyValueParam).Compile();
}

public static Action<object, object> PropertySet(this Type source, string propertyName)
{
    return source.PropertySet<object>(propertyName);
}

 Last thing that should be fix is null checking for GetMethod and SetMethod - after all it is already done in previous examples. This require single if in PropertySet and PropertyGet methods.

var propertyInfo = GetPropertyInfo(source, propertyName);
if (propertyInfo?.GetMethod == null)
{
    return null;
}

var propertyInfo = GetPropertyInfo(source, propertyName);
if (propertyInfo?.SetMethod == null)
{
    return null;
}

If either property of accessor do not exists (after all properties do not always have both set and get accessor) method will return null instead of delegate.

 

Indexers

Indexers are just special type of instance properties. Best way to thing about them is as special kind of get and set accessors, with extra index parameters. And they actually are special properties with name "Item". We should start with creating indexer in our test class.

private readonly List<int> _indexerBackend = new List<int>(10);

public int this[int i]
{
    get
    {
        return _indexerBackend[i];
    }
    set { _indexerBackend[i] = value; }
}

Yes there is no index checking, nor automatic collection resizing, etc. It is just test code so it do not need to be safe.

This is really easy case and delegate for this will be also everything but complex. We need Func<TestInstance,int,int> for this indexer. Simplest way to create it with single call will be like below.

var ig1 = DelegateFactory.IndexerGet<TestClass, int, int>();

How to implement IndexerGet method? Most important is to get PropertyInfo object for indexer.

private const string Item = "Item";

private static PropertyInfo GetIndexerPropertyInfo(Type source, Type returnType, Type[] indexesTypes) { var propertyInfo = (source.GetProperty(Item, returnType, indexesTypes) ?? source.GetProperty(Item, BindingFlags.NonPublic, null, returnType, indexesTypes, null)) ?? source.GetProperty(Item, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, returnType, indexesTypes, null); return propertyInfo; }

For public indexer way to obtain PropertyInfo is almost the same as for property - of course there is need for two new parameters that indicate return and indexes type of desired indexer. For non-public indexer things get a little more complicated since we have to use more general overload. Of course all of calls to GetProperty method use constant value "Item" as name of property. With property info we can create delegate for indexer with single index parameter very similar to delegate for ordinary property.

public static Func<TSource, TIndex, TReturn> IndexerGet<TSource, TReturn, TIndex>()
{
    var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex) });
    return (Func<TSource, TIndex, TReturn>)
            propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TReturn>));
}

As you can see the main difference is that instead just one parameter with instance (like in properties) there are one more extra with index parameter. Beside that it is pretty much the same.

Ok, but what if indexer have more than one index? How about two or three? It is possible too and since IndexerGet method takes type parameters we cannot make more universal method that returns different delegate for 2, 3, 4 or more parameters. For each of those types of indexers there is necessity for another methods with different set of types parameters. Since indexers are rarely used and mostly for indexing operations of some kind, we will create methods for only up to 3 index parameters (for up to 3D arrays).

public static Func<TSource, TIndex, TIndex2, TReturn> IndexerGet<TSource, TReturn, TIndex, TIndex2>()
{
    var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2) });
    return (Func<TSource, TIndex, TIndex2, TReturn>)
            propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TIndex2, TReturn>));
}

public static Func<TSource, TIndex, TIndex2, TIndex2, TReturn> IndexerGet<TSource, TReturn, TIndex, TIndex2, TIndex3>()
{
    var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2), typeof(TIndex3) });
    return (Func<TSource, TIndex, TIndex2, TIndex2, TReturn>)
            propertyInfo?.GetMethod?.CreateDelegate(typeof(Func<TSource, TIndex, TIndex2, TIndex2, TReturn>));
}

Above method will spawn delegates for two and three indexes and are called very similar to 1D array indexer overload. There is of course possibility of writing methods that returns delegates for more indexes, up to 16 - it is limit of Func class.

var ig1 = DelegateFactory.IndexerGet<TestClass, int, int, int>();
var ig2 = DelegateFactory.IndexerGet<TestClass, int, int, int, int>();

As you probably already noticed all of above methods for indexers getters can only create delegates only if we know type of class. If we do not (because it is private or not know it in compile-time) we need a method that takes Type parameter instead. We can use the same extension method convention as before with properties.

var ig1 = Type.IndexerGet<int, int>();
var ig2 = Type.IndexerGet<int, int, int>();
var ig3 = Type.IndexerGet<int, int, int, int>();

The same problem occurs with indexers as with properties before. Since we do pass instance and not the type itself we can't create delegates with known type of parameter - it needs to be object. Because of that we again need expressions. We will start with indexer with single index parameter.

public static Func<object, TIndex, TReturn> IndexerGet<TReturn, TIndex>(this Type source)
{
    var indexType = typeof(TIndex);
    var returnType = typeof(TReturn);
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, new[] { indexType });
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var paramExpression = Expression.Parameter(indexType);
    return (Func<object, TIndex, TReturn>)Expression.Lambda(
        Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramExpression), sourceObjectParam, paramExpression).Compile();
}

It is very similar to PropertyGet method that creates delegate that takes object as source instance. Difference is again with index parameter. Because it is an object there is need for conversion first. After creating lambda expression that takes instance object and index parameters, it is then compiled and returned as delegate. It can be represented as following lambda statement.

Func<object, int, int> @delegate = (o, i) => ((TestClass)o)[i];

Ok. How about two or three indexes? Luckily Expression.Lambda and Expression.Call methods, have overloads that takes unspecified number of parameters. With that we can easily rewrite most of the code for creating delegate, to more universal version. We should write method that creates Delegate instance by compiling expression from indexer return type and indexes types.

public static Delegate DelegateIndexerGet(Type source, Type returnType, params Type[] indexTypes)
{
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes);
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var paramsExpression = new ParameterExpression[indexTypes.Length];
    for (var i = 0; i < indexTypes.Length; i++)
    {
        var indexType = indexTypes[i];
        paramsExpression[i] = Expression.Parameter(indexType);
    }
    return Expression.Lambda(
                Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramsExpression),
                new[] { sourceObjectParam }.Concat(paramsExpression)).Compile();
}

Most important change is that this method takes unspecified number of indexes types. Second change is that instead of known number of parameters in Lambda and Call we have collection of parameters (with one more in lambda - source instance parameter). Aside of that it is the same code as before and with this new method we can implement tree extension methods above.

public static Func<object, TIndex, TReturn> IndexerGet<TReturn, TIndex>(this Type source)
{
    var indexType = typeof(TIndex);
    return (Func<object, TIndex, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType);
}
        
public static Func<object, TIndex, TIndex2, TReturn> IndexerGet<TReturn, TIndex, TIndex2>(this Type source)
{
    var indexType = typeof(TIndex);
    var indexType2 = typeof(TIndex2);
    return (Func<object, TIndex, TIndex2, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType, indexType2);
}

public static Func<object, TIndex, TIndex2, TIndex3, TReturn> IndexerGet<TReturn, TIndex, TIndex2, TIndex3>(this Type source)
{
    var indexType = typeof(TIndex);
    var indexType2 = typeof(TIndex2);
    var indexType3 = typeof(TIndex3);
    return (Func<object, TIndex, TIndex2, TIndex3, TReturn>)DelegateIndexerGet(source, typeof(TReturn), indexType, indexType2, indexType3);
}

It is straightforward code. Type parameters are changed to instances of Type class and passed to DelegateIndexerGet method. Returned value is casted to expected Func class. But those new method still lacks some features though. Indexers are not required to have primitive index types - they can be anything. Even if this is rarely the case we have to have way to create delegates for more indexing parameters. But much more important is return type of indexer - it can any type and it is more common. We need more generic method then. For starters we can create method that creates delegate Func<object,object,object> for indexers that have only one parameter.

public static Func<object, object, object> IndexerGet(this Type source, Type returnType, Type indexType)
{
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, new[] { indexType });
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var indexObjectParam = Expression.Parameter(typeof(object));
    Expression returnExpression = Expression.Call(Expression.Convert(sourceObjectParam, source),
        propertyInfo.GetMethod, Expression.Convert(indexObjectParam, indexType));
    if (!propertyInfo.PropertyType.IsClass)
    {
        returnExpression = Expression.Convert(returnExpression, typeof(object));
    }
    return (Func<object, object, object>)
        Expression.Lambda(returnExpression, sourceObjectParam, indexObjectParam).Compile();
}

It is almost the same as PropertyGet overload that works on objects instead of concrete types - it just takes one parameter more.

Ideally would be to have a way to get all of indexer types in uniform way. It is possible, for example, to write delegate that takes two parameters: instance and array of indexes and returns object.

var ig = Type.IndexerGet(typeof(int), typeof(int), typeof(int), typeof(int));
var t = ig(TestInstance, new object[] { 0, 0, 0 });

Problem with this method is that it requires much more calculation which involves reading array of indexes, converting them to correct types, calling indexer getter and converting its value to object. Because all of that it will be much slower than just call to getter, but if we do not have luxury of more strict delegate (like Func<TSource, TIndex, TReturn>) it is best we can do.

public static Func<object, object[], object> IndexerGet(this Type source, Type returnType, params Type[] indexTypes)
{
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes);
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var indexesParam = Expression.Parameter(typeof(object[]));
    var paramsExpression = new Expression[indexTypes.Length];
    for (var i = 0; i < indexTypes.Length; i++)
    {
        var indexType = indexTypes[i];
        paramsExpression[i] = Expression.Convert(Expression.ArrayIndex(indexesParam, Expression.Constant(i)), indexType);
    }
    Expression returnExpression =
        Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.GetMethod, paramsExpression);
    if (!propertyInfo.PropertyType.IsClass)
    {
        returnExpression = Expression.Convert(returnExpression, typeof(object));
    }
    return (Func<object, object[], object>)Expression.Lambda(
        returnExpression, sourceObjectParam, indexesParam).Compile();
}

Main change from previous methods is that we have single parameter with all indexes passed to lambda in ParameterExpression.

var indexesParam = Expression.Parameter(typeof(object[]));

Since this is an array of indexes we need to get every single one of them from it. It can be done by Expression.ArrayIndex method. After retrieving index value we need to cast it to index type at the same position. With collection of converted indexes we can pass it to Expression.Call as collection of indexer getter parameters. There is similar change (if we comparing code with PropertyGet method without type parameters), that returns object instead of property type instance: if return type is value type (int, byte etc.) conversion to object is done. Here is translation to more common lambda statement.

Func<object, object[], object> @delegate = (o, i) => (object)((TestClass)o)[(int)i[0], (int)i[1], (int)i[3]];

Yes, of course there is no checking if indexes or instance is of correct type. There is no checking if array of indexes have correct size nor check for index types. There plenty of things that may go wrong. But we do write delegates for speed not for safety. And if we are talking about performance it is about time to test it. We should add few indexers to TestClass first.

public int this[int i1, int i2, int i3]
{
    get { return i1; }
    set
    {
    }
}

internal string this[string s] => s;

private long this[long s] => s;

private int this[int i1, int i2]
{
    get { return i1; }
    set { }
}

Test code for above indexers and first one looks like this.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = TestInstance[0];
}
_stopWatch.Stop();
Console.WriteLine("Public indexer: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = ig1(TestInstance, 0);
}
_stopWatch.Stop();
Console.WriteLine("Public indexer retriever: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = TestInstance[0, 0, 0];
}
_stopWatch.Stop();
Console.WriteLine("Multiple index indexer directly: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = ig10(TestInstance, new object[] { 0, 0, 0 });
}
_stopWatch.Stop();
Console.WriteLine("Multiple index indexer via delegate with array: {0}", _stopWatch.ElapsedMilliseconds);

var indexerInfo = Type.GetProperty("Item", typeof(int), new Type[] { typeof(int), typeof(int), typeof(int) });
_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = indexerInfo.GetValue(TestInstance, new object[] { 0, 0, 0 });
}
_stopWatch.Stop();
Console.WriteLine("Multiple index indexer via reflection: {0}", _stopWatch.ElapsedMilliseconds);

Running this code will results in console output as below.

Public indexer: 1555
Public indexer retriever: 1603
Multiple index indexer directly: 525 Multiple index indexer via delegate with array: 5847 Multiple index indexer via reflection: 44755

As you can see calling indexer by proxy delegate is not that much slower. Of course for more complex indexers then calling indexing operation on backend field like in first indexer in TestClass will make this difference even less visible. Another thing worth mentioning is that calling delegate with array instead of set of parameters make it much slower than original indexer. In test indexer it is about ten times slower. Difference is that big because indexer itself is just single return operation when delegate have to index and convert all of parameters and pass it to indexer. Difference would probably be even greater for indexers with more than three parameters (another thing is why write such indexer then instead of single method). From another hand, if indexer would be more complex difference would be less visible. Anyway still delegate is few times quicker than reflection call.

 

Improvements

In some cases there might be small problem. Indexer name not necessarily have to be an 'Item'. According to documentation it can be changed like this.

[System.Runtime.CompilerServices.IndexerName("TheItem")]
internal string this[string s] => s;

Do anybody ever used that? Smile I do not think that it is very common, but it do not matter. We can make it work anyway. We could write new overloads of our methods with indexer name parameter, but it would require too much work and since we are smart and lazy there is a better way Wink. There is possibility of renaming indexer property, but you have to do it for all indexers at once in the same class. GetIndexerPropertyInfo method already looks for indexer info few times. Why do not force it to do that few more times? If 'Item' is not there, we can search for other properties that are indexers.

private static PropertyInfo GetIndexerPropertyInfo(Type source, Type returnType, Type[] indexesTypes,
    string indexerName = null)
{
    indexerName = indexerName ?? Item;

    var propertyInfo = (source.GetProperty(indexerName, returnType, indexesTypes) ??
                        source.GetProperty(indexerName, BindingFlags.NonPublic, null,
                            returnType, indexesTypes, null)) ??
                        source.GetProperty(indexerName,
                            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
                            null, returnType, indexesTypes, null);
    if (propertyInfo != null)
    {
        return propertyInfo;
    }
    var indexer = source.GetProperties().FirstOrDefault(p => p.GetIndexParameters().Length > 0);
    return indexer != null ? GetIndexerPropertyInfo(source, returnType, indexesTypes, indexer.Name) : null;
}

Indexer properties returned from reflection have not null array returned from method GetIndexParameters. This way we can find name for all of indexer and then do more specific search for the one we really looking for. And end user of DelegatesFactory do not have to think about name of an indexer at all (which is good since indexers do not have names in C# code).

To complete subject about an indexers getters we need to check for propertyInfo variable and PropertyInfo.Get/SetMethod properties if they are not null. It should be done the same way as with properties.

if (propertyInfo?.GetMethod == null)
{
    return null;
}

 

Setters

Now we can jump to indexers setters. It is very similar situation like with setters for properties. First we use Action classes instead of Func, second we have one more parameter for value to set in indexer. That is all. For example, first IndexerGet method after conversion to IndexerSet looks like this.

public static Action<TSource, TIndex, TProperty> IndexerSet<TSource, TIndex, TProperty>()
{
    var sourceType = typeof(TSource);
    var propertyInfo = GetIndexerPropertyInfo(sourceType, typeof(TProperty), new[] { typeof(TIndex) });
    return (Action<TSource, TIndex, TProperty>)
        propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TIndex, TProperty>));
}

Methods for 2D and 3D indexers are also very similar.

public static Action<TSource, TIndex, TIndex2, TReturn> IndexerSet<TSource, TReturn, TIndex, TIndex2>()
{
    var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2) });
    return (Action<TSource, TIndex, TIndex2, TReturn>)
            propertyInfo?.SetMethod?.CreateDelegate(typeof(Action<TSource, TIndex, TIndex2, TReturn>));
}

public static Action<TSource, TIndex, TIndex2, TIndex2, TReturn> IndexerSet<TSource, TReturn, TIndex, TIndex2, TIndex3>()
{
    var propertyInfo = GetIndexerPropertyInfo(typeof(TSource), typeof(TReturn), new[] { typeof(TIndex), typeof(TIndex2), typeof(TIndex3) });
    return (Action<TSource, TIndex, TIndex2, TIndex2, TReturn>)
            propertyInfo?.SetMethod?.CreateDelegate(
                typeof(Action<TSource, TIndex, TIndex2, TIndex2, TReturn>));
}

With method with array of indexes it is a little more complicated.

public static Action<object, object[], object> IndexerSet(this Type source, Type returnType, params Type[] indexTypes)
{
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes);
    if (propertyInfo?.SetMethod == null)
    {
        return null;
    }
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var indexesParam = Expression.Parameter(typeof(object[]));
    var valueParam = Expression.Parameter(typeof(object));
    var paramsExpression = new Expression[indexTypes.Length + 1];
    for (var i = 0; i < indexTypes.Length; i++)
    {
        var indexType = indexTypes[i];
        paramsExpression[i] = Expression.Convert(Expression.ArrayIndex(indexesParam, Expression.Constant(i)), indexType);
    }
    paramsExpression[indexTypes.Length] = Expression.Convert(valueParam, returnType);
    Expression returnExpression =
        Expression.Call(Expression.Convert(sourceObjectParam, source), propertyInfo.SetMethod, paramsExpression);
    return (Action<object, object[], object>)Expression.Lambda(
        returnExpression, sourceObjectParam, indexesParam, valueParam).Compile();
}

First difference is of course Action instead of Func. Second is that returnType param is used in Expression.Convert to change type of valueParam parameter, which is last parameter of Expression.Call.

var valueParam = Expression.Parameter(typeof(object));
paramsExpression[indexTypes.Length] = Expression.Convert(valueParam, returnType)

The same valueParam variable is passed as last parameter (after array of indexes) of Expression.Lambda method. Everything else is similar to code in PropertyGet, PropertySet and IndexerGet.

With extension methods changes are in new DelegateIndexerSet method.

public static Delegate DelegateIndexerSet(Type source, Type returnType,
    params Type[] indexTypes)
{
    var propertyInfo = GetIndexerPropertyInfo(source, returnType, indexTypes);
    if (propertyInfo?.SetMethod == null)
    {
        return null;
    }
    var sourceObjectParam = Expression.Parameter(typeof(object));
    var valueParam = Expression.Parameter(returnType);
    var indexExpressions = new ParameterExpression[indexTypes.Length];
    for (var i = 0; i < indexTypes.Length; i++)
    {
        var indexType = indexTypes[i];
        indexExpressions[i] = Expression.Parameter(indexType);
    }
    var callArgs = indexExpressions.Concat(new [] { valueParam }).ToArray();
    var paramsExpressions = new[] { sourceObjectParam }.Concat(callArgs);
    return Expression.Lambda(
                Expression.Call(Expression.Convert(sourceObjectParam, source),
                    propertyInfo.SetMethod, callArgs), paramsExpressions).Compile();
}

Of course there is SetMethod used instead of getter equivalent. Also there is new variable valueParam as in method above. And new collections, callArgs and paramesExpressions, that allows us to pass appropriate values to lamba and setter calls. First one, is collection of indexes parameters expressions and value expression. Second one is source instance parameter expression, the same indexes expressions, and value parameter at the end.

And finally we can write last of IndexerSet methods with new DelegateIndexerSet method.

public static Action<object, TIndex, TReturn> IndexerSet<TReturn, TIndex>(this Type source)
{
    var indexType = typeof(TIndex);
    return (Action<object, TIndex, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType);
}

public static Action<object, TIndex, TIndex2, TReturn> IndexerSet<TReturn, TIndex, TIndex2>(this Type source)
{
    var indexType = typeof(TIndex);
    var indexType2 = typeof(TIndex2);
    return (Action<object, TIndex, TIndex2, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType, indexType2);
}

public static Action<object, TIndex, TIndex2, TIndex3, TReturn> IndexerSet<TReturn, TIndex, TIndex2, TIndex3>(this Type source)
{
    var indexType = typeof(TIndex);
    var indexType2 = typeof(TIndex2);
    var indexType3 = typeof(TIndex3);
    return (Action<object, TIndex, TIndex2, TIndex3, TReturn>)DelegateIndexerSet(source, typeof(TReturn), indexType, indexType2, indexType3);
}

Those methods are called exactly the same as the ones for get accessors. They just create different delegates.

var is1 = DelegateFactory.IndexerSet<TestClass, int, int>();
var is2 = DelegateFactory.IndexerSet<TestClass, int, int, int>();
var is3 = DelegateFactory.IndexerSet<TestClass, int, int, int, int>();
var is4 = Type.IndexerSet(typeof(int), typeof(int), typeof(int), typeof(int));
var is5 = Type.IndexerSet<int, int>();
var is6 = Type.IndexerSet<int, int, int>();
var is7 = Type.IndexerSet<int, int, int, int>();

is1(TestInstance, 0, 1);
is2(TestInstance, 0, 0, 1);
is3(TestInstance, 0, 0, 0, 1);
is4(TestInstance, new object[] { 0, 0, 0 }, 1);
is5(TestInstance, 1, 2);
is6(TestInstance, 0, 0, 2);
is7(TestInstance, 0, 0, 0, 2);

 

Static Fields

We have properties and indexers covered. Now we should try to make delegates for fields. First the static ones. How to do it? With properties (or methods for that matter) we have MethodInfo objects for each accessor, but fields are fields and they do not have accessor, which are really only methods with special name. Because of that we cannot create delegates from FieldInfo returned from reflection. We can do that only by creating delegates from expressions. There is Expression.Field exactly for that kind of data access.

But before implementing any of new methods. It is about time to write some test static fields.

public static string StaticPublicField = "StaticPublicField";

public static string StaticInternalField = "StaticInternalField";

public static string StaticProtectedField = "StaticProtectedField";

public static string StaticPrivateField = "StaticPrivateField";

 

Get static field value

New methods for creating delegates that will return value of a static field will be called StaticFieldGet. First we will write overload that take type as type param - for known types at compile time. They will be called in following way.

var sfg1 = DelegateFactory.StaticFieldGet<TestClass, string>("StaticPublicField");

Simplest implementation would look like below.

public static Func<TField> StaticFieldGet<TSource, TField>(string fieldName)
{
    var source = typeof(TSource);
    var lambda = Expression.Lambda(Expression.Field(null, source, fieldName));
    return (Func<TField>)lambda.Compile();
}

Much simpler in contrast to some indexer expressions for delegates. In lambda statement the same expression is even more clear.

Func<string> @delegate = () => TestClass.StaticPublicField;

Simple function that returns value. Ok, but what will happen if field name is invalid? Expression will fail and application will crash. With code like that we can't really remedy this in any way beside try-catch statement. Luckily there is another overload of Expression.Field method that takes FieldInfo object instead of a string with field name. We can search for desired field ourselves. If field is not found and FieldInfo object is null, null value will be returned instead of a delegate from StaticFieldGet method. To retrieve field data we can create new method GetFieldInfo based on code from GetPropertyInfo method.

private static FieldInfo GetStaticFieldInfo(Type source, string fieldName)
{
    var fieldInfo = (source.GetField(fieldName, BindingFlags.Static) ??
                          source.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)) ??
source.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return fieldInfo; }

And now we can write safer method using field info.

public static Func<TField> StaticFieldGet<TSource, TField>(string fieldName)
{
    var fieldInfo = GetStaticFieldInfo(typeof(TSource), fieldName);
    if (fieldInfo != null)
    {
        var lambda = Expression.Lambda(Expression.Field(null, fieldInfo));
        return (Func<TField>)lambda.Compile();
    }
    return null;
}

There is null check and another overload of Expression.Field method with field data as parameter. Rest of the code was already covered in different methods.

We can also test both methods for performance difference if pure expression version would be renamed to i.e. StaticFieldGetExpr.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < 10000; i++)
{
    DelegateFactory.StaticFieldGet<TestClass, string>("StaticPublicField");
}
_stopWatch.Stop();
Console.WriteLine("Static public field creator via reflection: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < 10000; i++)
{
    DelegateFactory.StaticFieldGetExpr<TestClass, string>("StaticPublicField");
}
_stopWatch.Stop();
Console.WriteLine("Static public field creator via expression: {0}", _stopWatch.ElapsedMilliseconds);

According to performance test, the difference is either negligible or with small indication of reflection one as faster.

Static public field creator via reflection: 507
Static public field creator via expression: 483

But again, what if we do not have either source type or field type or even both? After all fields are almost always private and can have private types.

As before we can fix that by adding new overloads.

var sfg1 = Type.StaticFieldGet<string>("StaticPublicField");
var sfg2 = Type.StaticFieldGet("StaticPublicField");

The same as with properties and indexers, first overload should return field type and second just object.

public static Func<TField> StaticFieldGet<TField>(this Type source,
    string fieldName)
{
    var fieldInfo = GetStaticFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var lambda = Expression.Lambda(Expression.Field(null, fieldInfo));
        return (Func<TField>)lambda.Compile();
    }
    return null;
}

public static Func<object> StaticFieldGet(this Type source, string fieldName)
{
    var fieldInfo = GetStaticFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        Expression returnExpression = Expression.Field(null, fieldInfo);
        if (!fieldInfo.FieldType.IsClass)
        {
            returnExpression = Expression.Convert(returnExpression, typeof(object));
        }
        var lambda = Expression.Lambda(returnExpression);
        return (Func<object>)lambda.Compile();
    }
    return null;
}

If field have value type conversion to object is performed. Beside that it is almost the same code as in previous overload. Because of, that previous method can be simplified like this.

public static Func StaticFieldGet<TSource, TField>(string fieldName)
{
    var source = typeof(TSource);
    return source.StaticFieldGet(fieldName);
}

Now, in the same and easy manner we can retrieve fields with all visibilities.

var sfg3 = Type.StaticFieldGet<string>("StaticPublicField");
var sfg4 = Type.StaticFieldGet<string>("StaticInternalField");
var sfg5 = Type.StaticFieldGet<string>("StaticProtectedField");
var sfg6 = Type.StaticFieldGet<string>("StaticPrivateField");

Console.WriteLine("Static public field value: {0}", sfg3());
Console.WriteLine("Static internal field value: {0}", sfg4());
Console.WriteLine("Static protected field value: {0}", sfg5());
Console.WriteLine("Static private field value: {0}", sfg6());

After running this in test console application we will set correct values of all fields.

Static public field value: StaticPublicField
Static internal field value: StaticInternalField
Static protected field value: StaticProtectedField
Static private field value: StaticPrivateField

 

Set static field value

We now have a way to retrieve values of static fields. What about possibility of changing them? It can be done and as before with properties and indexers. Most of the code stays the same. Of course returned delegates would be of Action class. There is still Expression.Field in play, but need to be combined with Expression.Assign to be able to change field value with assignment operation.

For retrieving fields values we had three methods: static, extension method with type of field and extension method that returns an object. For changing fields we will write also three overloads.

var sfs1 = DelegateFactory.StaticFieldSet<TestClass, string>("StaticPublicField");
var sfs2 = Type.StaticFieldSet<string>("StaticPublicField");
var sfs3 = Type.StaticFieldSet("StaticPublicField");

Since first one can be easily rewritten as proxy to second one, let's start implementation with second one.

public static Action<TField> StaticFieldSet<TField>(this Type source,
    string fieldName)
{
    var fieldInfo = GetStaticFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var valueParam = Expression.Parameter(typeof(TField));
        var lambda = Expression.Lambda(typeof(Action<TField>),
            Expression.Assign(Expression.Field(null, fieldInfo), valueParam), valueParam);
        return (Action<TField>)lambda.Compile();
    }
    return null;
}

As you can see most of a change is in expression itself since there is not a single call to Expression.Field, but it is nested in Expression.Assign to mark that lambda body assign value parameter to field. Of course produced delegate is Action not a Func. For TestClass.StaticPublicField field it would produce lambda like below.

Action<string> @delegate = v => TestClass.StaticPublicField = v;

First overload will look like this.

public static Action<TField> StaticFieldSet<TSource, TField>(string fieldName)
{
    var source = typeof(TSource);
    return source.StaticFieldSet<TField>(fieldName);
}

Third one is much more interesting, but still nothing really hard to understand after indexers.

public static Action<object> StaticFieldSet(this Type source,
    string fieldName)
{
    var fieldInfo = GetStaticFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var valueParam = Expression.Parameter(typeof(object));
        var convertedValueExpr = Expression.Convert(valueParam, fieldInfo.FieldType);
        var lambda = Expression.Lambda(typeof(Action<object>),
            Expression.Assign(Expression.Field(null, fieldInfo), convertedValueExpr), valueParam);
        return (Action<object>)lambda.Compile();
    }
    return null;
}

As expected value parameter expression is in this method of type object instead of the same type as field. Because of that it needs to be converted first, before assignment. The same delegate as lambda for TestClass.StaticPublicField would be similar to lambda below.

Action<object> @delegate = v => TestClass.StaticPublicField = (string)v;

Time to test if those methods works.

var sfs1 = DelegateFactory.StaticFieldSet<TestClass, string>("StaticPublicField");
var sfs2 = Type.StaticFieldSet<string>("StaticPublicField");
var sfs3 = Type.StaticFieldSet("StaticPublicField");

sfs1("test1");
Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField);
sfs2("test2");
Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField);
sfs3("test3");
Console.WriteLine("Static public field value: {0}", TestClass.StaticPublicField);

Above code will result with console output as below.

Static public field value: test1
Static public field value: test2
Static public field value: test3

 

 

Fields

We already practiced on static fields so instance fields should not be a problem. Main change is extra parameter in all delegates. Because of that we need not null first parameter (source instance) in Expression.Field call. Beside that everything stays the same.

We should add some fields first to TestClass type.

public string PublicField;

internal string InternalField;

protected string ProtectedField;

private string _privateField;

public TestClass()
{
    PublicField = "PublicField";
    InternalField = "InternalField";
    ProtectedField = "ProtectedField";
    _privateField = "_privateField";
}

 

Get field value delegates

 Of course we want the same three methods for instance fields.

var fg1 = DelegateFactory.FieldGet<TestClass, string>("PublicField");
var fg2 = Type.FieldGet<string>("PublicField");
var fg3 = Type.FieldGet("PublicField");

First we have to create similar method to obtain FieldInfo object like in GetStaticFieldInfo.

private static FieldInfo GetFieldInfo(Type source, string fieldName)
{
    var fieldInfo = (source.GetField(fieldName) ??
                          source.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic)) ??
                          source.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    return fieldInfo;
}

Almost the same but it use Instance value of BindingFlags enumeration instead of Static one.

Let's start with last one FieldGet method this time - it should be most complex.

public static Func<object, object> FieldGet(this Type source, string fieldName)
{
    var fieldInfo = GetFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var sourceParam = Expression.Parameter(typeof(object));
        Expression returnExpression = Expression.Field(Expression.Convert(sourceParam, source), fieldInfo);
        if (!fieldInfo.FieldType.IsClass)
        {
            returnExpression = Expression.Convert(returnExpression, typeof(object));
        }
        var lambda = Expression.Lambda(returnExpression, sourceParam);
        return (Func<object, object>)lambda.Compile();
    }
    return null;
}

It is similar code to PropertyGet method and StaticFieldGet. Returned Func takes instance to retrieve field as object cast it to correct instance type and retrieves field value, which is casted to object if it is value type i.e. for TestClass.PublicField it will create delegate like this.

Func<object, object> @delegate = i => ((TestClass)i).PublicField;

Now, the same method can create delegate for two other methods just by safe cast.

public static Func<TSource, TField> FieldGet<TSource, TField>(string fieldName)
{
    var source = typeof(TSource);
    return source.FieldGet(fieldName) as Func<TSource, TField>;
}

public static Func<object, TField> FieldGet<TField>(this Type source,
    string fieldName)
{
    return source.FieldGet(fieldName) as Func<object, TField>;
}

I do not really investigate it further why this works (for example with ILDASM application) but I suspect that excessive casting get optimized by JIT compilation, because there is not really performance difference between produced delegates.

We can test new methods by following lines in test console application.

var fg1 = DelegateFactory.FieldGet<TestClass, string>("PublicField");
var fg2 = DelegateFactory.FieldGet<TestClass, string>("InternalField");
var fg3 = DelegateFactory.FieldGet<TestClass, string>("ProtectedField");
var fg4 = DelegateFactory.FieldGet<TestClass, string>("_privateField");

var fg5 = Type.FieldGet<string>("PublicField");
var fg6 = Type.FieldGet("PublicField");

Console.WriteLine("Public field value: {0}", fg1(TestInstance));
Console.WriteLine("Internal field value: {0}", fg2(TestInstance));
Console.WriteLine("Protected field value: {0}", fg3(TestInstance));
Console.WriteLine("Private field value: {0}", fg4(TestInstance));
Console.WriteLine("Public field value by object and field type: {0}", fg5(TestInstance));
Console.WriteLine("Public field value by objects: {0}", fg6(TestInstance));

It will produce following output in Windows console.

Public field value: PublicField
Internal field value: InternalField
Protected field value: ProtectedField
Private field value: _privateField
Public field value by object and field type: PublicField
Public field value by objects: PublicField

We can also test performance difference between direct field access and by proxy delegate.

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = TestInstance.PublicField;
}
_stopWatch.Stop();
Console.WriteLine("Public field directly: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = fg1(TestInstance);
}
_stopWatch.Stop();
Console.WriteLine("Public field retriever: {0}", _stopWatch.ElapsedMilliseconds);

var fieldInfo = Type.GetField("PublicField");
_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    var test = fieldInfo.GetValue(TestInstance);
}
_stopWatch.Stop();
Console.WriteLine("Public field via reflection: {0}", _stopWatch.ElapsedMilliseconds);

This performance test will produce similar result.

Public field directly: 257
Public field retriever: 545
Public field via reflection: 9721

As you can see difference is huge - more than 100%. But after all accessing public fields do not make sense this way, unless you do not know type at compilation time or field is not public at all. In those cases it is better to have slower access but access nevertheless. Unless you are fine with even bigger performance difference with use of reflection which is about 20 times slower.

 

Set field value delegates

Since we already trained setting of values in properties and in static fields, setting values in instance fields should not be a problem.

We still want the similar three methods: static, extension by objects and extension with field type.

var fs1 = DelegateFactory.FieldSet2<TestClass, string>("_privateField");
var fs2 = Type.FieldSet<string>("_privateField");          
var fs3 = Type.FieldSet("PublicField");

 Ok this time we again will start with last one that should be more complex. First two should be able to cast delegate from third.

public static Action<object, object> FieldSet(this Type source, string fieldName)
{
    var fieldInfo = GetFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var sourceParam = Expression.Parameter(typeof(object));
        var valueParam = Expression.Parameter(typeof(object));
        var convertedValueExpr = Expression.Convert(valueParam, fieldInfo.FieldType);
        Expression returnExpression = Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), convertedValueExpr);
        if (!fieldInfo.FieldType.IsClass)
        {
            returnExpression = Expression.Convert(returnExpression, typeof(object));
        }
        var lambda = Expression.Lambda(typeof(Action<object, object>),
            returnExpression, sourceParam, valueParam);
        return (Action<object, object>)lambda.Compile();
    }
    return null;
}

As with instance properties we have delegate in form of an Action with two parameters: first instance and then value to set. Both as objects. Similarly to static field we assigning value to field by Expressio.Assign and Expression.Field. Field have to take converted source parameter to instance type, instead of a null like in static field. Beside that code is very similar. Two other methods can be implemented by simple cast as in FieldGet.

public static Action<object, TProperty> FieldSet<TProperty>(this Type source, string fieldName)
{
    return source.FieldSet(fieldName) as Action<object, TProperty>;
}

public static Action<TSource, TProperty> FieldSet<TSource, TProperty>(string fieldName)
{
    return typeof(TSource).FieldSet(fieldName) as Action<TSource, TProperty>;
}

It works, but according to tests it is a bit slower than specific implementation. Version with delegate created directly, without casting are as follows.

public static Action<object, TProperty> FieldSet2<TProperty>(this Type source, string fieldName)
{
    var fieldInfo = GetFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var sourceParam = Expression.Parameter(typeof(object));
        var valueParam = Expression.Parameter(typeof(TProperty));
        var te = Expression.Lambda(typeof(Action<object, TProperty>),
            Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), valueParam),
            sourceParam, valueParam);
        return (Action<object, TProperty>)te.Compile();
    }
    return null;
}

public static Action<TSource, TProperty> FieldSet2<TSource, TProperty>(string fieldName)
{
    var source = typeof(TSource);
    var fieldInfo = GetFieldInfo(source, fieldName);
    if (fieldInfo != null)
    {
        var sourceParam = Expression.Parameter(source);
        var valueParam = Expression.Parameter(typeof(TProperty));
        var te = Expression.Lambda(typeof(Action<TSource, TProperty>),
            Expression.Assign(Expression.Field(sourceParam, fieldInfo), valueParam),
            sourceParam, valueParam);
        return (Action<TSource, TProperty>)te.Compile();
    }
    return null;
}

Code for performance test, that led to this conclusion, looks like this.

var fs1 = DelegateFactory.FieldSet<TestClass, string>("PublicField");
var fs2 = DelegateFactory.FieldSet2<TestClass, string>("PublicField");

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    fs1(TestInstance, "test");
}
_stopWatch.Stop();
Console.WriteLine("Public field set retriever with cast: {0}", _stopWatch.ElapsedMilliseconds);

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{
    fs2(TestInstance, "test");
}
_stopWatch.Stop();
Console.WriteLine("Public field set retriever without cast: {0}", _stopWatch.ElapsedMilliseconds);

Which will result in text below.

Public field set retriever with cast: 751
Public field set retriever without cast: 475

As you can see difference is noticeable. Probably instead of using the same method, but with parameter casting/without casting, correct delegate is created by creating new method that casts/converts parameters to appropriate types.

Ok, we will use FieldSet2 methods and not FieldSet overloads but after renaming then to FieldSet and FieldSetWithCast accordingly.

 

Improvements

If you were wondering about readonly keyword in fields it is a good thing. This thing should be covered in similar way like null check for getter or setter method in properties. After all readonly field is a similar to get-only property. Is there a way find out if field is declared with readonly keyword? As you surely remember readonly fields can be either set in field declaration or in constructor. Both ways are executed on object initialization. And on FieldInfo object is property called IsInitOnly which indicate if field can be set anytime or just during initialization - which another way of telling if field is readonly. This means that instead of just null check in fieldInfo variable we also need to check for this property. In both static fields and instance fields.

if (fieldInfo != null && !fieldInfo.IsInitOnly)

Of course it is needed in all StaticFieldSet and FieldSet methods  i.e.

public static Action<object, TProperty> FieldSet<TProperty>(this Type source, string fieldName)
{
    var fieldInfo = GetFieldInfo(source, fieldName);
    if (fieldInfo != null && !fieldInfo.IsInitOnly)
    {
        var sourceParam = Expression.Parameter(typeof(object));
        var valueParam = Expression.Parameter(typeof(TProperty));
        var te = Expression.Lambda(typeof(Action<object, TProperty>),
            Expression.Assign(Expression.Field(Expression.Convert(sourceParam, source), fieldInfo), valueParam),
            sourceParam, valueParam);
        return (Action<object, TProperty>)te.Compile();
    }
    return null;
}

 

Summary

Now we have a way to retrieve data from types by fields and properties, both static and instance. We also covered indexers which are special instance properties. What is left are events (instance not static), methods and constructors. Those types of members will be covered in next article.

Code for article can be found on github.



ViewFactory for Xamarin application

clock July 9, 2016 08:37 by author n.podbielski

First things first - Why?

First of all I do not like how creating of views and navigation works in Xamarin.

There is much boilerplate needed to just navigate from one page to another.

Typically like in Xamarin documentation we have to obtain INavigation interface instance, create instance of a new page, create instance of view model and all dependencies it requires.

When you page do not require view model things are real easy and Xamarin sample is just enough.

await Navigation.PushAsync(new LoginPage());

Right?! Easy! You want to use this framework because it so intuitive! Smile

When your page do not require sophisticated initialization nor view model have dependencies things are easy too.

await Navigation.PushAsync(new LoginPage(){ BindingContext = new LoginViewModel() });

Yes, it is possible to create view model instance via Xaml of course.

<Page.BindingContext>
    <LoginViewModel />
</Page.BindingContext>

And this is why you should never do that:

  1. Xaml intellisense is quite poor currently (as far as I know because I am using R#, which takes care of most of that)
  2. It is possible to write bad Xaml and you will NOT have any compile-time errors (and you are using strongly typed language, C# just for that, to get that kind of feedback). Those errors can be caught much later, in test or if view is rarely used, it can even travel to end user.
  3. Since you will not get any errors, you will have to specifically remember to add parameters, or other type of view model initialization. Did you ever tried to add constructor parameters to Xaml? It is possible, but complicated and I do not understand why would anyone tried that if this can be done easier and safer in code.

 

We should do it in code then.

When your view model have some dependencies (only 2 is not that much really) things becomes looks ugly.

await Navigation.PushAsync(new LoginPage()
    {
          BindingContext = new LoginViewModel(){
              AuthorizationService = new AutorizationService(new WebService(), new DataBase());
          };
    }
);

What if you navigate to the same page from more than one place? Do you create static method? It is viable solution to move view model creation to view constructor for example. Then, you could write only one line, like in example from Xamarin documentation, each time you want to navigate to this page. But still, if you have set of services and some of them are singletons, you will have to obtain instance of each of them in view or view model (much better) and change of dependencies will require few lines more. Lines that you probably wrote someplace else already. If you are lazy, like me, you probably do not want to repeat yourself. Smile

Of course in official documentation in samples INavigation instance is taken from VisualElement.Navigation, but if you need to pass some parameters between pages (simplest example is to pass customer instance from previous page with list of customers, to show details of this customer in next one) this solution becomes problematic. If you bind some data to view model and want to pass it to next one, you will need to obtain that data by casting BindingContext to appropriate type, create next view model instance and set required data to it.

await Navigation.PushAsync(new MainPage()
    {
          BindingContext = new MainViewModel(){
              User = ((LoginViewModel)BindingContext).Login,
              DataBase = new DataBase(),
              GPSService = new GPSService(),
              CameraService = new CameraService(),
          };
    }
);

This is just to show problems with navigation implemented like that.

There is really simple solution to dependencies problem. It is widely used and called IoC. With container, you can create view models and set dependencies via constructor or properties. It also perfectly doable to use IoC container to obtain view model dependencies in view or in view model constructor. But again it is bad practice. IoC container should be (in ideal world) used once, for example to create first view and then work in background. Having that in mind view is not best place to use it, since Xaml requirement is, that views do not have parameters in constructors. View model it is then.

 

What would be better

Best way to navigate between views in view models is in my opinion one line with one method

Navigate<MainViewModel>();

And if you need special initialization or passed parameters from previous view model:

Navigate<MainViewModel>((mvm)=>mvm.User = Login);

Would you not agree, that this looks much nicer?

But how to do that? Of course best solution is for Navigate method to do all the initialization of view and view model, setting BindingContext to view model and pushing it to INavigation instance. Overload with action would do extra initialization of new view model.

  1. Obtain correct view for given view model type.
  2. Create view instance.
  3. Create view model instance and get all its dependencies from IoC.
  4. Set view model in view
  5. Set new view to Master page if necessary.
  6. Obtain INavigation instance and push newly created view on top of that.

 

In this article we will cover first two points, fourth and partially third one. If you are wondering what Master page is all about, you should read my previous article. In summary: we want seamless views creation and navigation between them, regardless if there are only detail views (with menu, top bar, bottom status bar etc.) or whole screen views (without menu, top bar, bottom status bar etc.). Rest will be covered in next article.

 

Implementation of ViewFactory

To ease our start with new application we can reuse some code from Master page article. This code is available on github.

To show creation of ordinary pages and detail pages, main application need only two menu items, two views and application file. We can start by creating ViewFactory shared xamarin project and add files from previous article, with just little changes.

 

File App.xaml have almost the same content as before.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ViewFactory.App">
</Application>

Code file App.xaml.cs is very similar too.

using CustomMasterDetailControl;

namespace ViewFactory
{
    public partial class App
    {
        public App()
        {
            InitializeComponent();
            MainPage = MasterDetailControl.Create<MasterDetail, MasterDetailViewModel>();
        }
    }
}

Detail.xaml and MasterDetail.xaml are almost the same as Detail1.xaml and MasterDetail from previous article, respectively.

<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ViewFactory.Detail">
    <Label Text="Detail" VerticalOptions="Center" HorizontalOptions="Center" />
</customMasterDetailControl:DetailPage>
<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ViewFactory.Detail">
    <Label Text="Detail" VerticalOptions="Center" HorizontalOptions="Center" />
</customMasterDetailControl:DetailPage>

MasterDetailViewModel have only two commands to navigate to detail page and ordinary page. For now it just creates and set both pages as detail views, but we will change that after implementing ViewFactory class.

using System.Windows.Input;
using CustomMasterDetailControl;
using Xamarin.Forms;

namespace ViewFactory
{
    public class MasterDetailViewModel : MasterDetailControlViewModel
    {
        private ICommand _toDetai;

        private ICommand _toOrdinaryPage;

        public ICommand ToDetail
        {
            get { return _toDetai ?? (_toDetai = new Command(OnToDetail)); }
        }

        public ICommand ToOrdinaryPage
        {
            get { return _toOrdinaryPage ?? (_toOrdinaryPage = new Command(OnToOrdinaryPage)); }
        }

        private void OnToOrdinaryPage()
        {
            Detail = new OrdinaryPage();
        }

        private void OnToDetail()
        {
            Detail = new Detail();
        }
    }
}

OrdinaryPage derives from new UIPage class which serve as base class for DetailPage (base class of Detail page in Detail.xaml file). This way we can use one single base class for all of our views. It makes ViewFactory class a little simpler, but if it is impossible in your project, it will be simple enough to make this work with different one or even more than one base class for both types of views.

And OrdinaryPage also just have a single label inside.

<?xml version="1.0" encoding="utf-8" ?>
<masterDetail:UIPage xmlns="http://xamarin.com/schemas/2014/forms"
                         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                         xmlns:masterDetail="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                         x:Class="ViewFactory.OrdinaryPage">
  <Label Text="Ordinary page" VerticalOptions="Center" HorizontalOptions="Center" />
</masterDetail:UIPage>

One last touch to make this first version work: we need to copy and change a little CustomMasterDetailControl project.

 

BaseViewModel class is exactly what it seems - it only have implementation of INotifyPropertyChanged interface and we will need this base class for all of our view models. It is used in MasterDetailControlViewModel class instead of standalone implementation.

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace CustomMasterDetailControl
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

MasterDetailControlViewModel class now derives from BaseViewModel.

public class MasterDetailControlViewModel : BaseViewModel, INavigation

UIPage as mentioned above will serve as base class for all views, both partial and for whole screen:

using Xamarin.Forms;

namespace CustomMasterDetailControl
{
    public class UIPage : ContentPage
    {
    }
}
namespace CustomMasterDetailControl
{
    public class DetailPage : UIPage
    {
        public DetailPage()
        {
            SideContentVisible = true;
        }

        public bool SideContentVisible { get; set; }
    }
}

Ok. Now we can implement ViewFactory class. First thing to do is to create relations between view model types and view types. To do that, we need to scan all types and find all views. We can do it in Init method of ViewFactory class.

public static void Init()
{
    var appAssebly = Assembly.GetExecutingAssembly();
    var pages = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<UIPage>());
    var viewModels = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<BaseViewModel>()).ToArray();
    foreach (var page in pages)
    {
        var viewModel = viewModels.FirstOrDefault(vm => vm.Name == page.Name + "Model");
        if (viewModel != null)
        {
            Views.Add(viewModel, new ViewData
            {
                IsDetail = page.IfHaveBaseClassOf<DetailPage>(),
                ViewModelType = viewModel,
                ViewType = page
            });
        }
    }
}

ViewData class is just really simple data object.

public class ViewData
{
    public Type ViewType { get; set; }
    public Type ViewModelType { get; set; }
    public bool IsDetail { get; set; }
}

What is IfHaveBaseClassOf method? It is just simple Type type extension method. Contains logic for searching all base types in inheritance tree, to check if any of them is the one we are looking for.

public static bool IfHaveBaseClassOf(this Type type) where TBase : class
{
    var haveBase = false;
    var typeToCheck = type;
    var baseType = typeof(TBase);
    while (typeToCheck != null)
    {
        if (typeToCheck.BaseType == baseType)
        {
            haveBase = true;
            break;
        }
        else
        {
            typeToCheck = typeToCheck.BaseType;
        }
    }
    return haveBase;
}

It takes some type and checks if any base type in tree of inheritance is of specified type we want to find. It allows us to go through every type in assembly and check if it derive from BaseViewModel or UIPage classes which both are needed for our ViewFactory. Oh, and there is a check if view is detail view (derive from DetailPage) also with this method.

With relations between views and view models we can implement method for creating views.

public UIPage CreateView<TViewModel>() where TViewModel : BaseViewModel
{
    return CreateView(typeof(TViewModel));
}

public UIPage CreateView(Type viewModelType)
{
    if (Views.ContainsKey(viewModelType))
    {
        var viewData = Views[viewModelType];
        var page = (UIPage)Activator.CreateInstance(viewData.ViewType);
        var viewModel = Activator.CreateInstance(viewModelType);
        page.BindingContext = viewModel;
        return page;
    }
    return null;
}

As you can see it is really simple. It takes either type parameter or parameter with view model type. Then it find view type and instantiate both via Activator class. With view model it can became somewhat problematic if it does have dependencies, but it can easily be changed with IoC container, which would resolve view model with dependencies. Last thing it does, is setting view model as binding context for view.

This is just enough to have first working application with this solution. With just small change to MasterDetailViewModel commands we can now navigate via view models instead of views.

public class MasterDetailViewModel : MasterDetailControlViewModel
{
    private readonly ViewFactory.ViewFactory _viewFactory;

    private ICommand _toDetai;

    private ICommand _toOrdinaryPage;

    public MasterDetailViewModel()
    {
        _viewFactory = new ViewFactory.ViewFactory();
    }

    public ICommand ToDetail
    {
        get { return _toDetai ?? (_toDetai = new Command(OnToDetail)); }
    }

    public ICommand ToOrdinaryPage
    {
        get { return _toOrdinaryPage ?? (_toOrdinaryPage = new Command(OnToOrdinaryPage)); }
    }

    private void OnToDetail()
    {
        Detail = _viewFactory.CreateView<DetailPage>();
    }

    private void OnToOrdinaryPage()
    {
        Navigation.PushAsync(_viewFactory.CreateView<OrdinaryPage>());
    }
}

After starting this application we will see something similar to this:

 

First button will take us to detail page (menu still will be visible) and second one will take us to page for whole screen (without menu).

 

Things to improve

But there are still few points for improvements:

  1. Faster type instantiation instead of Activator
  2. Use of views and view models from libraries
  3. Some views might need specific view model type
  4. We can disable reading of all or only specific assemblies

 

Faster Views creation

Instead of using Activator.CreateInstence it is a bit faster (according to this post on StackOverflow) to use Expression.Lambda. My tests proves that it is true and compiled expression is about 25% faster than Activator.CreateInstance method. To use that technic we need to create delegate for each view. It is also possible for view model but it would not be good idea for constructors with parameters (which is something we want to do but with IoC). Let us change a little Init method and ViewData class. In ViewData we just have to add property for creator delegate:

public Func Creator { get; set; }

We need to fill this property in Init method:

Creator = (Func<UIPage>)Expression.Lambda(Expression.New(page.GetConstructor(Type.EmptyTypes))).Compile(),

It is not completely safe code, since Expression.New method expects not null parameter and Type.GetConstructor method can return null, if there is no constructor specified with given parameters (in this particular case without parameters). However this will happed only when view do not have default parameterless constructor, which view should have anyway. Anyway, in such a case Activator.CreateInstance would throw error too.

 

External libraries

Sometimes we have views and view models in different assembly than application assembly. Obviously, we want to be able to navigate to those views too. It is very easy to handle them with small change to our code. We only need to iterate through all of application assemblies and scan them as we do for application assembly. For this we can extract this scanning logic to new ScanAssembly method:

private static void ScanAssembly(Assembly appAssebly)
{
    var pages = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<UIPage>());
    var viewModels = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<BaseViewModel>()).ToArray();
    foreach (var page in pages)
    {
        var viewModel = viewModels.FirstOrDefault(vm => vm.Name == page.Name + "Model");
        if (viewModel != null)
        {
            Views.Add(viewModel, new ViewData
            {
                IsDetail = page.IfHaveBaseClassOf<DetailPage>(),
                ViewModelType = viewModel,
                ViewType = page,
                Creator =
                    (Func<UIPage>)Expression.Lambda(Expression.New(page.GetConstructor(Type.EmptyTypes))).Compile(),
            });
        }
    }
}

And we just need to iterate through all of assemblies in Init method.

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
    ScanAssembly(assembly);
}

With this our ViewFactory will register all views for available view models in all of assemblies.

 

Overriding external views

But sometimes, if you have some library with predefined views and view models for them (for login page i.e.), you do not want to use default view and instead use other one that will be more suitable for application. It would be nice to be able to override those default views. Best way to do that, would be to create view with the same name in application assembly and let ViewFactory do the rest - scanning and overriding the default.

First, we have to modify a little our scanning method.

private static void ScanAssembly(Assembly appAssebly, bool @override = false)
{
    var pages = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<UIPage>());
    var viewModels = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<BaseViewModel>()).ToArray();
    foreach (var page in pages)
    {
        var viewModel = viewModels.FirstOrDefault(vm => vm.Name == page.Name + "Model");
        if (viewModel != null)
        {
            if (!Views.ContainsKey(viewModel))
            {
                Views.Add(viewModel, GetViewData(viewModel, page));
            }
        }
        //no view model in assembly and if app assembly -> override
        else if (@override)
        {
            var viewToOverride = Views.FirstOrDefault(kv => kv.Key.Name == page.Name + "Model");
            if (viewToOverride.Key != null)
            {
                Views[viewToOverride.Key] = GetViewData(viewToOverride.Value.ViewModelType, page);
            }
        }
    }
}

New else clause do exactly that. If view model is not found in assembly, and this assembly is main application assembly (override parameter is equal true), ViewFactory looks through its current registration list and override if there is already view model with name similar to view name.

Before last improvement (scanning only specific assemblies), we can test other, new features.

We can create new PCL project named ExternalAssembly with ExternalView, ToOverrideView and appropriate view models for them.

 

In ExternalView and ToOverrideView files we have following content:

<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ExternalAssembly.ExternalView">
    <Label Text="External page" VerticalOptions="Center" HorizontalOptions="Center"
           FontSize="40"/>
</customMasterDetailControl:DetailPage>
<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ExternalAssembly.ToOverrideView">
    <Label Text="Not overriden view" VerticalOptions="Center" HorizontalOptions="Center"
           FontSize="40"/>
</customMasterDetailControl:DetailPage>

Nothing really fancy - just a big labels with simple text, which is just enough to see if this works. View models for those views are just classes that derives from BaseViewModel.

For ViewFactory, to be able to see new project, we need to add reference to ViewFactory.Droid project. Also we need to add buttons to navigate to those new views and commands to handle them:

<Button Text="To external page" Command="{Binding ToExternalPage}" />
<Button Text="To overriden page" Command="{Binding ToOverridenPage}" />
private ICommand _toExternalPage;
private ICommand _toOverridenPage;

public ICommand ToExternalPage
{
    get { return _toExternalPage ?? (_toExternalPage = new Command(OnToExternal)); }
}

public ICommand ToOverridenPage
{
    get { return _toOverridenPage ?? (_toOverridenPage = new Command(OnToOverriden)); }
}

private void OnToExternal()
{
    Detail = _viewFactory.CreateView<ExternalViewModel>();
}

private void OnToOverriden()
{
    Detail = _viewFactory.CreateView<ToOverrideViewModel>();
}

After running this code and trying new buttons we will see something similar to following screenshots.

Now we should try to override ToOverrideView. To do that we need to create view with the same name in ViewFactory.Droid assembly.

<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ViewFactory.ToOverrideView">
    <Label Text="Overriden view" VerticalOptions="Center" HorizontalOptions="Center"
           FontSize="40" />
</customMasterDetailControl:DetailPage>

It is almost the same as previous one with different label text. We just need to start an application and all the magic will happen automatically Smile. After clicking on last button (To overriden page) we will see different label this time.

 

 

We do not need all assemblies

Another point for improvements will be to disable scanning of all application assemblies. We don't need to scan System.Xml and other .NET dlls. One of the solutions would be to ignore those assemblies. It would not be hard to just skip them in ViewFactory, but still there are many libraries and Nuget packages that do not have views and they would be scanned anyway. To be true, there are much more assemblies without views in application than with views. Logical solution is then to look for assemblies with them to be scanned later. For example it is possible to add assembly attribute to mark assemblies with views for scanning. Simple one as below is enough.

using System;

namespace ViewFactory
{
    [AttributeUsage(AttributeTargets.Assembly)]
    public class ViewAssemblyAttribute : Attribute
    {
    }
}

But there is one problem. Since we need to mark i.e. ExternalAssembly as assembly with views we need this attribute to be accessible in this project. So we need third project with this attribute so we can reference it in ExternalAssembly. Even then new project with just a single attribute do not makes sense. Best thing would be to create new project with ViewFactory and other types it needs.
Unfortunately it is not that easy. Ideally it would be to place everything in PCL project, but portable version of .NET is very limited. There is no way to get list of assemblies, executing assembly, even acquiring base type of type is more complicated. There are two solutions we can use to get around those limitations.

  1. Split everything into PCL project and platform specific projects (with full .NET). Minus, big minus, of this solution is that you need to implement everything that requires full .NET more than once. But if you will write bigger framework for your applications you will do that at some point anyway, and if you have set of assemblies like that, you can do that very easily. IoC also makes this easier since you can create services for every specific part of .NET support that do not exists in PCL version (like IAdvancedReflection for part of reflection support that exists only in full .NET)
  2. Or you can try to remedy lacks of some properties or methods by combination of reflection, delegates, and expressions.

 

Since the first solution is really boring (and more easy, safe, testable etc.) we will cover second one first Smile. On aside note, it makes sense to use it only for single properties or methods. Writing bigger parts functionality like this would be really hard.

Remember TypesExtension class with single method IfHaveBaseClassOf? It wont work in PCL, without changes because portable .NET lacks Type.BaseType property. Oh, there is BaseType property, but on TypeInfo type. And TypeInfo have AsType method that returns corresponding Type object. But BaseType property returns Type value and there is need to cast it to TypeInfo again.

public static bool IfHaveBaseClassOf<TBase>(this Type type) where TBase : class
{
    var haveBase = false;
    var typeToCheck = type.GetTypeInfo();
    var baseType = typeof(TBase);
    while (typeToCheck != null)
    {
        if (typeToCheck.BaseType == baseType)
        {
            haveBase = true;
            break;
        }
        typeToCheck = typeToCheck.BaseType.GetTypeInfo();
    }
    return haveBase;
}

Ok, this is just juggling Type and TypeInfo, even if Type have BaseType property which is just unavailable in PCL. We could use reflection to get this property value.

var baseType = (Type)typeof(Type).GetRuntimeProperty("BaseType").GetValue(type);

But whole point of moving this to separate project was to make things faster, by reducing list of assemblies! This way, certainly, it would not become any faster. How we make it be better, then? We can make ourselves function to retrieve BaseType, the same way as we did with views constructors. There is even Expression.Property method just for that.

var parameterExpression = Expression.Parameter(typeof(Type));
GetBaseTypeFunc = (Func<Type, Type>)Expression.Lambda(Expression.Property(parameterExpression, "BaseType"), parameterExpression).Compile();

This creates delegate of function that takes Type object and returns its property BaseType. Parameter expression is used twice, because it is mentioned twice in lambda: first as parameter declaration and the as use. This may be a little confusing (at least was for me at first) so this is how this would be defined as 'usual' lambda.

(Type type)=>type.BaseType

According to my tests it is quicker than using portable .NET TypeInfo.AsType(), Type.GetTypeInfo() and TypeInfo.BaseType members. 1 million of executions of PCL version takes about 130ms against 110ms with use of delegate for getting BaseType value directly from Type. Not much of a difference, but since this method is used a lot it is worth a shot.

To cache this function we can save it to static field in static constructor.

static TypeExtensions()
{
    var parameterExpression = Expression.Parameter(typeof(Type));
    GetBaseTypeFunc = (Func<Type, Type>)Expression.Lambda(Expression.Property(
        parameterExpression, "BaseType"), parameterExpression).Compile();
}

And it is used exactly the same as BaseType property:

public static bool IfHaveBaseClassOf(this Type type) where TBase : class
{
    var haveBase = false;
    var typeToCheck = type;
    var baseType = typeof(TBase);
    while (typeToCheck != null)
    {
        if (GetBaseTypeFunc(typeToCheck) == baseType)
        {
            haveBase = true;
            break;
        }
        typeToCheck = GetBaseTypeFunc(typeToCheck);
    }
    return haveBase;
}

Ok. But this is only one missing property and code that mitigate this limitation is quite complex. Also code like that do not get checked at compile time. But splitting it in to PCL and platform specific implementations would be quite complex since it is static class and we cannot just make it abstract and override single method. Making it instance class would not make things easier too, since we can't use platform specific implementation directly in PCL. It would force us to write interface in PCL and then use its platform implementation via for example static reference in App class. This would be even more complex than compiling the delegate, and quite few things to do just to use one unavailable property. With solution as above we at least have it contained into one class and one file and we can forget about this inconvenience. It works and with few tests for this method it should be safe.

Splitting rest of classes between PCL and platform implementation will not be that complicated. TypeExtensions, ViewAssemblyAttribute, ViewData can be placed in PCL without changes. ViewFactory have to split into two parts. PCL version will be called BaseViewFactory and platform specific (like Android in sample) will be just ViewFactory.

There are two things that need platform specific implementation:

  1. Type.GetContstructor(Typep[]) method is not available in PCL
  2. AppDomain class is not available in PCL
  3. Assembly.GetExecutingAssembly method is not available in PCL

 

For first two we have to create abstract methods (and BaseViewFactory need to be abstract by extent).

protected abstract Assembly[] GetViewAssemblies();

protected abstract ConstructorInfo GetDefaultConstructor(Type page)

In platform version of ViewFactory we have:

public class ViewFactory : BaseViewFactory
{
    protected override Assembly[] GetViewAssemblies()
    {
        return AppDomain.CurrentDomain.GetAssemblies();
    }

    protected override ConstructorInfo GetDefaultConstructor(Type page)
    {
        return page.GetConstructor(Type.EmptyTypes);
    }
}

In summary it is the same code that was previously in single ViewFactory class, but now is called via abstract methods. Unfortunately calling those two method forces as to make Init method non static. And this forces us to make some static reference to ViewFactory in example in App class or use Singleton pattern. For case of sample simplicity static instance will suffice.

public partial class App
{
    public static readonly VF ViewFactory = new VF();
    public App()
    {
        InitializeComponent();
        ViewFactory.Init();
        MainPage = MasterDetailControl.CreateMainPage<MasterDetail, MasterDetailViewModel>();
        Navigation = MainPage.Navigation;
    }
}

As you can see it is just static global instance. Nothing fancy. IoC would allow us to make it much better.

Another problem with new code is that, since it do not reside in application assembly, we do not have easy access to it. Calling code like below in ViewFactory class (platform part):

Debug.WriteLine(Assembly.GetExecutingAssembly());
Debug.WriteLine(Assembly.GetCallingAssembly());
Debug.WriteLine(Assembly.GetEntryAssembly());

will give us following result:

[0:] ViewFactory.Android, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
[0:] ViewFactory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
[0:]

First is Android platform version of ViewFactory project, second is PCL ViewFactory assembly, and third is null. Of course this assembly is inside loaded assemblies list and we could find it by App or MainAcitivity types, but we would have to iterate both: assemblies and types in each of them, and we wanted not to do that.
Simple way to solve this issue it to just call Assembly.GetExecutingAssembly() inside application assembly and pass return value to ViewFactory. Let us change Init method once again and pass appropriate value inside App type constructor.

public void Init(Assembly appAssembly)

ViewFactory.Init(Assembly.GetExecutingAssembly());

And this is why we do not need to fix lack of Assembly.GetExecutingAssembly method in PCL. We do not need to call it there anyway.

 

Non standard view model - view connection

It might be case when there is need to have connection between view model and view other than by name convention (or if this is just impossible to rename view or view model). For this case it would be nice to have possibility of defining view type. We can solve this by adding new attribute for view models, that allows us to define desired view type.

[AttributeUsage(AttributeTargets.Class)]
public class ViewTypeAttribute : Attribute
{
    public ViewTypeAttribute(Type viewType)
    {
        ViewType = viewType;
    }

    public Type ViewType { get; }
}

This attribute it is used like this:

[ViewType(typeof(NoViewModelView))]
public class NoViewViewModel : BaseViewModel

Very easy and convenient. But applying it to view model forces us to change ScanAssembly method a little, so it reads view models first and then tries to found appropriate views for them.

private void ScanAssembly(Assembly appAssebly, bool @override = false)
{
    var pages = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<UIPage>()).ToArray();
    var viewModels = appAssebly.DefinedTypes.Where(t => t.IfHaveBaseClassOf<BaseViewModel>());

    foreach (var viewModel in viewModels)
    {
        var viewTypeAttribute = viewModel.GetCustomAttribute<ViewTypeAttribute>();
        var page = viewTypeAttribute != null 
            ? viewTypeAttribute.ViewType 
            : pages.FirstOrDefault(p => p.Name == viewModel.Name.Replace(ViewModelPrefix, ""))?.AsType();
        if (page != null)
        {
            var asType = viewModel.AsType();
            if (!Views.ContainsKey(asType))
            {
                Views.Add(asType, GetViewData(asType, page));
            }
        }
    }
    //no view model in assembly and if app assembly -> override
    if (@override)
    {
        foreach (var page in pages)
        {
            var viewToOverride = Views.FirstOrDefault(kv => kv.Key.Name == page.Name + ViewModelPrefix);
            if (viewToOverride.Key != null)
            {
                Views[viewToOverride.Key] = GetViewData(viewToOverride.Value.ViewModelType, page.AsType());
            }
        }
    }
}

Its pretty much the same code, but instead of iterating pages we are iterating view models.

New logic involves looking for above attribute. Code is mostly self-explanatory. First its search for attribute on view model and if it was applied just uses type defined there. If not it searches inside collection of pages in assembly. Since Assembly.DefinedTypes is collection of TypeInfo objects null propagation and AsType method is used.

var viewTypeAttribute = viewModel.GetCustomAttribute<ViewTypeAttribute>();
        var page = viewTypeAttribute != null 
            ? viewTypeAttribute.ViewType 
            : pages.FirstOrDefault(p => p.Name == viewModel.Name.Replace(ViewModelPrefix, ""))?.AsType();

There are some cases when several views are connected to the same view model. It might be some kind of wizard or other process that require several steps and each step is represent by different view. To minimize effect of state on application (many bugs in my code, including ones that are really hard to debug where connected to state of objects, corrupted or otherwise incorrect, so I can agree to some extent when someone is using phrase: 'state is evil') it is good to have the same view model for all of those views. How to accomplish that with our view factory?

The easiest way to do it is to have overload of CreateView method that takes view model and view types. Yes, it defeats purpose of view factory to get rid of views in view models navigation code, but is makes code much more readable and intuitive. Otherwise we would have to create some kind of mechanism of selecting of view from view model state that is transparent to view factory. How? It would be possible with special view model type, with method or property, that would return desired type of view. But it would be too rigid to force base class of view models just for that. More flexible would be to possibility of defining method or property visible to view factory that would return view type. In both we would have to deal with view types anyway. I was considering for some time creating attributes for views that would allow us to define method or property name and desired value returned that would indicate, that it is time for this particular view. I think that it would be viable since view model probably would have some internal state value, that would point to some particular step (number of step for example). However it would force us to place view model method name in attribute which would be hard to maintain and not very transparent.
So the most reasonable way is to just allow to define view type for view model in CreateView method.

public UIPage CreateView<TViewModel, TView>() where TViewModel : BaseViewModel
    where TView : UIPage
{
    var type = typeof(TView);
    if (!_unconnectedViews.ContainsKey(type))
    {
        _unconnectedViews[type] = GetViewCreator(type);
    }
    return CreateView(typeof(TViewModel), _unconnectedViews[type]);
}

public UIPage CreateView<TViewModel, TView>(TViewModel viewModel) where TViewModel : BaseViewModel
    where TView : UIPage
{
    var type = typeof(TView);
    if (!_unconnectedViews.ContainsKey(type))
    {
        _unconnectedViews[type] = GetViewCreator(type);
    }
    return CreateView(viewModel, _unconnectedViews[type]);
}

Those two new overloads are used, first navigate to wizard view model and view type (overload with two types) and then navigate to all subsequent steps (overload with view model instance and view type).

Private static CreateView methods needs to be changed too.

private static UIPage CreateView(Type viewModelType, Func<UIPage> creator)
{
    var viewModel = Activator.CreateInstance(viewModelType);
    return CreateView(viewModel, creator);
}

private static UIPage CreateView(object viewModel, Func<UIPage> creator)
{
    var page = creator();
    page.BindingContext = viewModel;
    return page;
}

Now we can test both, new features in action. First custom connection between view and view models. For this we should create new view and view model.

<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                                  x:Class="ViewFactorySample.View.NoViewModelView">
    <Label Text="No view model view" VerticalOptions="Center" HorizontalOptions="Center"
           FontSize="40"/>
</customMasterDetailControl:DetailPage>
[ViewType(typeof(NoViewModelView))]
public class NoViewViewModel : BaseViewModel
{

}

Nothing very different from previous views and view models. We need just to add new button in main view:

private ICommand _toCustomConnection;

public ICommand ToCustomConnection
{
    get { return _toCustomConnection ?? (_toCustomConnection = new Command(OnToCustomConnection)); }
}

private void OnToCustomConnection()
{
    Detail = _viewFactory.CreateView<NoViewViewModel>();
}
<Button Text="To custom connection page" Command="{Binding ToCustomConnection}" />

After running changed application and clicking on new button we will see page like below.

 

Custom connection created by attribute works.

Let's test navigating to views with the same view model.

For this we have to create new view model and (at least) two views. Second view is almost the same as previous ones (with different label text). First have one button more, to allow us to test navigation to second step.

<?xml version="1.0" encoding="utf-8" ?>
<customMasterDetailControl:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:customMasterDetailControl="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
             x:Class="ViewFactorySample.View.Step1View">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Text="Step 1" VerticalOptions="Center" HorizontalOptions="Center"
         FontSize="40" />
        <Button Grid.Row="1" Text="To next step" Command="{Binding ToNextStep}"></Button>
    </Grid>
</customMasterDetailControl:DetailPage>

ToNextStep command is handled in view model.

using System.Windows.Input;
using CustomMasterDetailControl;
using ViewFactorySample.View;
using Xamarin.Forms;

namespace ViewFactorySample.ViewModels
{
    public class WizardViewModel : BaseViewModel
    {
        private ICommand _toNextStep;

        public ICommand ToNextStep
        {
            get { return _toNextStep ?? (_toNextStep = new Command(OnToNextStep)); }
        }

        private void OnToNextStep()
        {
            var mainPage = ((NavigationPage)App.Current.MainPage).CurrentPage;
            var masterPageViewModel = (MasterDetailViewModel)mainPage.BindingContext;
            masterPageViewModel.Detail = masterPageViewModel.ViewFactory.CreateView<WizardViewModel, Step2View>(this);
        }
    }
}

As you can see command is pretty complicated, but almost all of the code is to retrieve reference to MasterPageViewModel to set new page as detail. This is major pain and I will cover in next article how to achieve easy and uniform navigation to ordinary, whole screen pages and detail ones.

Last thing to make this test works is new button on main page (exactly the same as before, with different text and command). It is handled a bit differently, to show how to create page with new CreateView overload.

private void OnToWizard()
{
    Detail = _viewFactory.CreateView<WizardViewModel, Step1View>();
}

After running application and using new menu button it will navigate to first step view. Button at bottom will then navigate to second step.

 

That is all. We have working ViewFactory class that allows us to create page - view model pairs in easily and configurable way. But as you can see from MasterDetailViewModel and WizardViewModel there are still some problems with navigation to whole screen pages and detail pages. Since code is different we need to know in two places if page is detail page or not. It is redundant since we already set that by choosing base class (UIPage or DetailPage). There is need for new mechanism that takes care of that - either set page to Detail property of MasterPage or push it to navigation. Also ViewFactory always creates view models with Activator.CreateInstance which require view model to have default, parameterless constructor. We do not want that, but to fix that we need IoC container to resolve view models with dependencies.

I will cover both topics in next articles.

Sample application you can find here ViewFactory.zip (83.71 kb)

Code for this article is also available on github as previous ones from Xamarin series.



Xamarin Master-Detail Page

clock April 24, 2016 03:41 by author n.podbielski

This article as previous ones (Frame With Padding, Frame Outline Problem, Inheritance from Implicit Styles) also will be about Xamarin.
This time we will inspect Master-Detail page control/mechanism. You can find official documentation for this control here.

Quick spoiler: I never used this in any production application. Why? I will explain. Whole article will be to explain why this is just not worth your time. In this and subsequent articles I will introduce much better solution.

Xamarin MasterDetailPage control

Ok lets get to work. For a test drive lets say we have to do an application that allows user to browse stuff on some online shop. Master-Detail page will be used to allow user switch to a different category of wares. According to documentation user have to introduce only 3 files (at least) to make this work. Since I am lazy I will not create whole menu but only Master view and 3 Detail views (we cannot waste time on creating nice UI just for test right? Smile)

First main application xaml file:

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:masterDetail="clr-namespace:MasterDetail;assembly=MasterDetail"
             x:Class="MasterDetail.App">
  <Application.MainPage>
    <masterDetail:MasterPage />
  </Application.MainPage>
</Application>

This cannot be more straightforward so there is no need to explain. Lets focus on MasterPage view instead:

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:masterDetail="clr-namespace:MasterDetail;assembly=MasterDetail"
             x:Class="MasterDetail.MasterPage">
  <MasterDetailPage.Master>
    <ContentPage Title = "Test">
      <StackLayout>
        <Button Text="Detail1" />
        <Button Text="Detail2" />
        <Button Text="Detail3" />
      </StackLayout>
    </ContentPage>
  </MasterDetailPage.Master>
  <MasterDetailPage.Detail>
    <NavigationPage>
      <x:Arguments>
        <masterDetail:Detail1 />
      </x:Arguments>
    </NavigationPage>
  </MasterDetailPage.Detail>
</MasterDetailPage>

This file is a little more complicated but after few seconds it is clear that intention is to have 3 'menu' buttons that send you to different Detail pages.

Single Detail page could look like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MasterDetail.Detail1">
  <Label Text="Detail1" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Again, nothing interesting just a label to see if correct Detail page is displayed.

Above application will start and display something similar to this:

 

Ok. I lied a little. Smile You need one more file to actually make navigation between Detail pages work. Ok lets handle this in MasterPage.xaml.cs code behind and create those details pages. For now two more files will be enough.

using System;
using Xamarin.Forms;

namespace MasterDetail
{
    public partial class MasterPage
    {
        public MasterPage()
        {
            InitializeComponent();
        }

        private void Button1_OnClicked(object sender, EventArgs e)
        {
            Detail = (new Detail1());
        }

        private void Button2_OnClicked(object sender, EventArgs e)
        {
            Detail = new NavigationPage(new Detail2());
        }

        private void Button3_OnClicked(object sender, EventArgs e)
        {
            Detail = new NavigationPage(new Detail3());
        }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MasterDetail.Detail3">
  <Label Text="Detail3" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MasterDetail.Detail2">
  <Label Text="Detail2" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

First: code behind that handles navigation. If you will read carefully you will notice some differences in handling detail pages. Two others are almost exactly the same as fist Detail page.

After running that solution you will see that there are really no difference between Detail1 and Detail2.

 

Why new NavigationPage() then? Because Xamarin documentation says:

The page type is wrapped in a NavigationPage instance in order to ensure that the icon referenced through the Icon property on the MasterPage is shown on the detail page in iOS.

Ok... But what if you do not write for iOS? They do not say. So you have to ensure iOS compatibility... you know just in case. Why you? Should not Xamarin take care of things like that?

Anyway, even after enclosing all of the detail pages inside a NavigationPage (even if just for having exactly the same view layout in all of them) it do not work quite well.

Why? Let us do quick experiment and press back button (if there is one on device) - it will cause application to go into background (or at least it does on Android). What is the cause of that? It works like that because there is change of NavigationPage with every Detail page and NavigationPage is object that handles... well navigation.

Luckily there is a rather easy fix for this too:

namespace MasterDetail
{
    public partial class MasterPage
    {
        public MasterPage()
        {
            InitializeComponent();
        }

        private void Button1_OnClicked(object sender, EventArgs e)
        {
            ChangeDetail(new Detail1());
        }

        private void Button2_OnClicked(object sender, EventArgs e)
        {
            ChangeDetail(new Detail2());
        }

        private void Button3_OnClicked(object sender, EventArgs e)
        {
            ChangeDetail(new Detail3());
        }

        private void ChangeDetail(Page page)
        {
            var navigationPage = Detail as NavigationPage;
            if (navigationPage != null)
            {
                navigationPage.PushAsync(page);
                return;
            }
            Detail = new NavigationPage(page);
        }
    }
}

Since there is a bit of logic involved a new method has been added - ChangeDetail. I think it is self explanatory: we just test if there is already NavigationPage and reuse that one.

This is again code that could be easily (and should be in my opinion) in Xamarin implementation of Detail property. But hey! There is Master-Detail mechanism right? If you unlucky to be unhappy with it - well you are unlucky. Xamarin Team certainly can't let you have virtual property Detail so you could do that a better way (they have rather unpleasant tendency to have everything internal/closed/sealed).

Let us continue main topic. Just for the sake of an example lets pretend that your manager, or marketing team, or anyone that is using your appm, thinks that application do not need that wide navigation menu. They think that 50 units will suffice and they ask if you can change it. You are starting maybe from setting it in StackLayout:

<StackLayout WidthRequest="50">
  <Button Text="Detail1" Clicked="Button1_OnClicked" />
  <Button Text="Detail2" Clicked="Button2_OnClicked" />
  <Button Text="Detail3" Clicked="Button3_OnClicked" />
</StackLayout>

But if does not work. Ok... Maybe this will work:

<ContentPage Title="Test" WidthRequest="50">
  <StackLayout WidthRequest="50">
    <Button Text="Detail1" Clicked="Button1_OnClicked" />
    <Button Text="Detail2" Clicked="Button2_OnClicked" />
    <Button Text="Detail3" Clicked="Button3_OnClicked" />
  </StackLayout>
</ContentPage>

No. Maybe MasterDetailPage.Master have property for this? Nope. Maybe MasterDetailPage? Nope. You can't do that. Xamarin developers did not think of that. You would have to forward the information that this change is impossible (or better easily done/would require one week of work Smile) for now.

After some time you maybe have another request, but with remark that this is absolutely necessary: menu have to go to the right edge.

You can check if MasterBehaviour property have something that can help you with that and according to documentation its enumeration type have following values:

  • Default – The pages are displayed using the platform default.
  • Popover – The detail page covers, or partially covers the master page.
  • Split – The master page is displayed on the left and the detail page is on the right.
  • SplitOnLandscape – A split screen is used when the device is in landscape orientation.
  • SplitOnPortrait – A split screen is used when the device is in portrait orientation.

As you see it does not anything helpful. Since there are only four other properties in this control you loose hope and explains to anyone requesting this change that it will need 2 weeks of works since you have to research new solution, implement and test it. At least.

Maybe I am pessimist but there are always changes and they in 90% about adding new features/code instead of removing so it is much more probable that you will have to change how Master and Detail mechanism work because of new requirement/device/theme etc.

Of course you can modify presentation by creating new renderer but it is really complex class and would only solve issues in one platform. No. This is not recommended solution. 

All those missing features and inability to configure anything is just only part of the problem. How many times you are writing application with Xaml files that uses only views without any view models? I personnally can't think of one. Even the smallest applications are written with few views and view models for them. Even models and database for small application are not that rare.

Lets think about above example. You have menu handlers in .xaml.cs file. I almost never use those and instead of them I use commands. Can you use them here? Not really, because you don't have access to Detail property in different object. You would have to create static property or method at least to allow yourself have access to property that is pretty much whole point of this control.
But static property can not be used in tests (at least easily) so better is to create special interface and have public access to its implementation somewhere. IoC container maybe? If you use this pattern you probably have IoC constructors in some classes, most probably in view models and those can't be created easily in view click handlers, like in above example, or in ListView.ItemSelected handler like Xamarin documentation suggests.

Best thing is to use commands and some interface in my opinion. This would be easiest thing to do to amend that discrepancy. But lets think of it from other angle.

I am big fan of separating classes in smaller parts for readability, easier maintenance, testability etc. The same thing can in my opinion can be said (to some extent) about xaml files. Can this be done in MasterPage.xaml file? For example you will decide to extract from there Master page and put it in Menu.xaml. Can you do that? Not easily because you can not move ButtonX_OnClicked handlers easily because they depend of MasterDetailPage.Detail property. Of course you can create interface the same as with view model problem, but again it shows that MasterDetailPage control creates more problems then it solves.

After reading about this control I decided not to use it because all of things covered above. In fact in my opinion this control is pretty much useless, But what to use instead?

ContentTemplate and ContentPresenter

First idea is to use newly added features of templating mechanism by ContentTemplate and ContentPresenter.

Let us add new button to MasterPage.xaml file:

<Button Text="Detail4" Clicked="Button4_OnClicked" />

To create ContentTemplate we have to add just one item to resources in our application, preferably in App.xaml:

<Application.Resources>
  <ResourceDictionary>
    <ControlTemplate x:Key="DetailTemplate">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="250"></ColumnDefinition>
          <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <StackLayout BackgroundColor="Red">
          <Button Text="Detail1"></Button>
          <Button Text="Detail2"></Button>
          <Button Text="Detail3"></Button>
        </StackLayout>
        <ContentPresenter Grid.Column="1">
            
        </ContentPresenter>
      </Grid>
    </ControlTemplate>
  </ResourceDictionary>
</Application.Resources>

There is only 3 buttons just to show that it is different view. Red background is for the same thing. There is no handling of buttons because of the problems with MasterDetailPage control explained above. It is just a demo for templating mechanism.

Creating forth detail page called Detail4 like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MasterDetail.Detail4"
             ControlTemplate="{StaticResource DetailTemplate}">
  <Label Text="Detail4" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

is enough for now.

Last thing is to handle button click MasterPage.xaml.cs:

private void Button4_OnClicked(object sender, EventArgs e)
{
    App.Current.MainPage = new Detail4();
}

Yes this is also handled in xaml code behind file, but since this uses static App.Current property it can be easily moved to view model command property.

After running and trying out new button we will see something like this.

 

We have 250 width, red background. We can edit this view in every way possible for any page. We will do not have any problems with previous example change requests.

But there is other problem. What if we will have use state somehow in a way of coloring or disabling current page button? It is not that easy. Since it is a template and not separate control we can't have different BindingContext for menu and Detail page. The same view model for both sooner or later will cause some problems and it will not be most obvious one, using the same view model for all or maybe different ones but with the same values for menu?

More complex problems will be caused by complexity of menu. If you will have to i.e. 2-level menu and collapse, expand sub menus user will almost sure will see that every time after opening new detail page.

This mechanism is more usable for theming application, reusing some external library control with styles for every application etc.

If not templates then what?

Custom MasterDetail control

Yes. If you want job done well, do it your self.

First of all, requirements:

1. Control should have default view model.

2. Changing detail pages must be handled from view models.

3. Control should be created once.

4. Control should handle back button.

5. View should be allowed to override when neccessary.

 

Seems easy, right? Smile

Lets start with creating App.xamlfile and its code behind file:

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomMasterDetail.App">
</Application>
namespace CustomMasterDetail
{
    public partial class App
    {
        public App()
        {
            InitializeComponent();
            MainPage = new MasterDetail();
        }
    }
}

Simple right? To make this correct we have to also create this MasterDetail class. But since view model is required, let us start there:

using System.ComponentModel;
using System.Runtime.CompilerServices;
 
namespace CustomMasterDetail.ViewModel
{
    public class MasterDetailViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

For now this can be any view model – it is just a minimal base implementation. We need to add Detail property:

private View _detail;
public View Detail
{
    get { return _detail; }
    set
    {
        _detail = value;
        OnPropertyChanged();
    }
}

If you wondering about OnPropertyChange call, yes it will be binded. It is not clearest implementation of View-ViewModel pattern but it is clear and simple and it will be enough for now.

Ok. Lets jump to view. We will start with simple xaml page:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomMasterDetail.MasterDetail">
</ContentPage>

There are two things missing:

  1. Menu
  2. Detail page presentation

To add them we need to create xaml definition for first and BindableProperty for second one. We will start with second one.

public readonly BindableProperty DetailProperty = BindableProperty.Create("Detail",typeof(ContentPage));
 
public ContentPage Detail
{
    get { return (ContentPage)GetValue(DetailProperty); }
    set { SetValue(DetailProperty, value); }
}

There is one thing that might be confusing. Detail property type is a ContentPage because of example simplicity. It could be also View because this is base type of any control that could be root of detail page layout (Grid, StackLayout etc.). Page would be also doable but it would require some magic to obtain root view from i.e. CarouselPage and still allow user to use its navigation features.

We will get back to this topic.

Menu will be the same as in previous examples.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <StackLayout Grid.Column="0">
        <Button Text="Detail1" />
        <Button Text="Detail2" />
        <Button Text="Detail3" />
    </StackLayout>
</Grid>

Grid control allows us to add menu and detail page and controls how they are connected in very simple but yet powerful way.

We need to connect Detail properties of control and view model and we can do that with simple binding:

public MasterDetail()
{
    InitializeComponent();
    SetBinding(DetailProperty, new Binding("Detail", BindingMode.OneWay));
}

We need to do this in a code behind and not in xaml because xaml operate in base class context and we need this in our class. Yes we could do this in other xaml file (in this case in App.xaml) but then we had to do this every single time we would use this control, which would really defeat whole purpose of creating separate control

Now we can define where Detail page will be in our layout and it will be second column of Grid. Of course this can any other place in Master page – top, right, bottom. You can even create multiple parts of master page that are permanently visible, in every detail page. Doing this way you can create i.e. extra properties in master view model and in detail view models that allows you hide, show this other parts, change displayed text, add buttons and etc. This is why it is better to do it this way instead of using Xamarin poor control.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <StackLayout Grid.Column="0">
        <Button Text="Detail1" />
        <Button Text="Detail2" />
        <Button Text="Detail3" />
    </StackLayout>
    <ContentView Grid.Column="1" x:Name="DetailContainer" />
</Grid>

Change of view model property. triggers change of View property. which is handled in BindableProperty definition via propertyChanged parameter:

public readonly BindableProperty DetailProperty = BindableProperty.Create("Detail",
    typeof(ContentPage), typeof(MasterDetailControl),
    propertyChanged: (bindable, value, newValue) =>
    {
        var masterPage = (MasterDetailControl)bindable;
        View content;
        var contentPage = (ContentPage)newValue;
        if (contentPage != null)
        {
            content = contentPage.Content;
            content.BindingContext = contentPage.BindingContext;
        }
        else content = null;
        masterPage.DetailContainer.Content = content;
        masterPage.OnPropertyChanged("SideContentVisible");
    });

Now it is much clearer why there is ContentPage type used for Detail properties in view and view model –  this type have Content property, which is set as Content of our newly added ContentView control named DetailContainer. It is important to mark why there is assignment of content BindingContext property - without it this value (in masterPage.DetailContainer.Content) would be resolved to parent value, which would be MasterDetailControl.BindingContext. We do not want that. Instead there is assigned directly detail view model to it, which is what is expected when you edit detail page xaml - it suppose to be this page view model.

Ok. As you can see we have easily configurable layout of control, Detail page easily accessible from ViewModel. However we do not have required support for INavigation interface right?

Let’s take care of it now. Easiest way will be to add this interface to view model:

public MasterDetailViewModel(INavigation navigation)
{
     _navigation = navigation;
}

private Stack _pages = new Stack();

public void InsertPageBefore(Page page, Page before)
{
    if (_pages.Contains(before))
    {
        var list = _pages.ToList();
        var indexOfBefore = list.IndexOf(before);
        list.Insert(indexOfBefore, page);
        _pages = new Stack(list);
    }
    else
    {
        _navigation.InsertPageBefore(page, before);
    }
}

public Task PopAsync()
{
    Page page = null;
    if (_pages.Count > 0)
    {
        page = _pages.Pop();
        _detail = page;
        OnPropertyChanged("Detail");
    }
    return page != null ? Task.FromResult(page) : _navigation.PopAsync();
}

public Task PopAsync(bool animated)
{
    Page page = null;
    if (_pages.Count > 0)
    {
        page = _pages.Pop();
        _detail = page;
        OnPropertyChanged("Detail");
    }
    return page != null ? Task.FromResult(page) : _navigation.PopAsync(animated);
}

public Task PopModalAsync()
{
    return _navigation.PopModalAsync();
}

public Task PopModalAsync(bool animated)
{
    return _navigation.PopModalAsync(animated);
}

public Task PopToRootAsync()
{
    var firstPage = _navigation.NavigationStack[0];
    if (firstPage is MasterDetail
        || firstPage.GetType() == typeof(MasterDetail))
    {
        _pages = new Stack(new[] { _pages.FirstOrDefault() });
        return Task.FromResult(firstPage);
    }
    return _navigation.PopToRootAsync();
}

public Task PopToRootAsync(bool animated)
{

    var firstPage = _navigation.NavigationStack[0];
    if (firstPage is MasterDetail
        || firstPage.GetType() == typeof(MasterDetail))
    {
        _pages = new Stack(new[] { _pages.FirstOrDefault() });
        return Task.FromResult(firstPage);
    }
    return _navigation.PopToRootAsync(animated);
}

public Task PushAsync(Page page)
{
    Detail = page;
    return Task.FromResult(page);
}

public Task PushAsync(Page page, bool animated)
{
    Detail = page;
    return Task.FromResult(page);
}

public Task PushModalAsync(Page page)
{
    return _navigation.PushModalAsync(page);
}

public Task PushModalAsync(Page page, bool animated)
{
    return _navigation.PushModalAsync(page, animated);
}

public void RemovePage(Page page)
{
    if (_pages.Contains(page))
    {
        var list = _pages.ToList();
        list.Remove(page);
        _pages = new Stack(list);
    }
    _navigation.RemovePage(page);
}

public IReadOnlyList ModalStack { get { return _navigation.ModalStack; } }

public IReadOnlyList NavigationStack
{
    get
    {
        if (_pages.Count == 0)
        {
            return _navigation.NavigationStack;
        }
        var implPages = _navigation.NavigationStack;
        MasterDetail master = null;
        var beforeMaster = implPages.TakeWhile(d =>
        {
            master = d as MasterDetail;
            return d is MasterDetail || d.GetType() == typeof(MasterDetail);
        }).ToList();
        beforeMaster.AddRange(_pages);
        beforeMaster.AddRange(implPages.Where(d => !beforeMaster.Contains(d)
            && d != master));
        return new ReadOnlyCollection(_navigation.NavigationStack.ToList());
    }
}

It may look a little complicated but most important are only 2 methods, PushAsync and PopAsync for navigation inside an application. Modal windows functionality is just redirected to Xamarin implementation of interface. ModalStack and NavigationStack properties may be usable sometimes. First just unifies Xamarin stack and MasterPage stack (from _pages field) into one. Rest of interface is not really helpfull in most of applications, but it is implemented as ax example even if would be possible to implement only PopAsync and PushAsync methods or even creating simpler interface only for navigation inside MasterPage.

With that kind of implementation of INavigation interface we can use it inside of OnBackButtonPressed override in MasterPage:

protected override bool OnBackButtonPressed()
{
    var viewModel = BindingContext as MasterDetailViewModel;
    if (viewModel != null)
    {
        var navigation = (INavigation)viewModel;
        navigation.PopAsync();
        return true;
    }
    return base.OnBackButtonPressed();
}

Above code fulfils our requirements. MasterDetail.xaml is easy to change and it is created only once. MasterDetailViewModel is specific view model for control and its INavigation implementation makes detail page navigation easily available from outside of control. The same interface allows handling of backbutton in control.

Next thing to do: it would be nice to have MasterDetail control in reusable portable library.

MasterDetailControl in PCL library

Placing our new control in separate PCL (Portable Class Library), allow to reuse it in multiple applications. It is very useful if you have multiple applications with similar themes. Then it is possible to place whole theme in single PCL.

Lets start with creating PCL project i.e. named CustomMasterDetailControl. We need to add xaml file and view model class to this project. Most of that code will be copied from previous example.

First, xaml file MasterDetailControl.

<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                x:Class="CustomMasterDetailControl.MasterDetailControl">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackLayout Grid.Column="0" x:Name="SideContentContainer" />
        <ContentView Grid.Column="1" x:Name="DetailContainer" />
    </Grid>
</ContentPage>

There is one change from previous version. Instead of having menu in StackLayout control we just assigning name to this control so now it is available in code behind.

public partial class MasterDetailControl
{
    public static readonly BindableProperty SideContentProperty = BindableProperty.Create("SideContent",
        typeof(View), typeof(MasterDetailControl), null, propertyChanged: (bindable, value, newValue) =>
        {
            var control = (MasterDetailControl)bindable;
            control.SideContentContainer.Children.Clear();
            control.SideContentContainer.Children.Add(control.SideContent);
        });

    public readonly BindableProperty DetailProperty = BindableProperty.Create("Detail",
    typeof(ContentPage), typeof(MasterDetailControl),
    propertyChanged: (bindable, value, newValue) =>
    {
        var masterPage = (MasterDetailControl)bindable;
        masterPage.DetailContainer.Content = newValue != null ?
            ((ContentPage)newValue).Content : null;
    });

    public MasterDetailControl()
    {
        InitializeComponent();
        SetBinding(DetailProperty, new Binding("Detail", BindingMode.OneWay));
    }

    public ContentPage Detail
    {
        get { return (ContentPage)GetValue(DetailProperty); }
        set { SetValue(DetailProperty, value); }
    }

    public View SideContent
    {
        get { return (View)GetValue(SideContentProperty); }
        set { SetValue(SideContentProperty, value); }
    }

    public static Page Create<TView, TViewModel>() where TView : MasterDetailControl, new()
        where TViewModel : MasterDetailControlViewModel, new()
    {
        var masterDetail = new TView();
        var navigationPage = new NavigationPage(masterDetail);
        var viewModel = new TViewModel();
        viewModel.SetNavigation(navigationPage.Navigation);
        masterDetail.BindingContext = viewModel;
        return navigationPage;
    }

    protected override bool OnBackButtonPressed()
    {
        var viewModel = BindingContext as MasterDetailControlViewModel;
        if (viewModel != null)
        {
            var navigation = (INavigation)viewModel;
            navigation.PopAsync();
            return true;
        }
        return base.OnBackButtonPressed();
    }
}

With Bindable property defined like this we can override side content of MasterDetailControl in our every application like this:

<MasterDetailControl.SideContent>
    <StackLayout>
        <Button Text="Detail1" Command="{Binding ToDetail1}" />
        <Button Text="Detail2" Command="{Binding ToDetail2}" />
        <Button Text="Detail3" Command="{Binding ToDetail3}" />
    </StackLayout>
</MasterDetailControl.SideContent>

Very easy and intuitive.

There is also new method that should be used to create instance of our control and override view model type for it:

public static Page Create<TView, TViewModel>() where TView : MasterDetailControl, new()
    where TViewModel : MasterDetailControlViewModel, new()
{
    var masterDetail = new TView();
    var navigationPage = new NavigationPage(masterDetail);
    var viewModel = new TViewModel();
    viewModel.SetNavigation(navigationPage.Navigation);
    masterDetail.BindingContext = viewModel;
    return navigationPage;
}

This method contains logic that was previously in App.xaml.cs file:

public App()
{
    InitializeComponent();
    var masterDetail = new MasterDetail();
    var navigationPage = new NavigationPage(masterDetail);
    MainPage = navigationPage;
    masterDetail.BindingContext = new MasterDetailViewModel(navigationPage.Navigation);
}

However there is no point in forcing this code in every application. Instead we can use new method in following way:

public App()
{
    InitializeComponent();
    MainPage = MasterDetailControl.Create<MasterDetail, MasterDetailViewModel>();
}

Best way to set MainPage of application in my opinion would be to set it in xaml file of App class, but this would force as to i.e. rewrite our control so it would derive from NavigationPage instead and then add the same Bindable properties to this new type to propagate those values to our actual control. I do not think that getting rid one line in code justifies that kind of complexity.

Of course if MasterDetailViewModel class do not have parameterless constructor (because of IoC i.e.) we can create new method that takes instance of view model type and change previous overload a little:

public static Page Create<TView, TViewModel>() where TView : MasterDetailControl, new()
    where TViewModel : MasterDetailControlViewModel, new()
{
    return Create<TView, TViewModel>(new TViewModel());
}

public static Page Create<TView, TViewModel>(TViewModel viewModel) where TView : MasterDetailControl, new()    
    where TViewModel : MasterDetailControlViewModel
{
    var masterDetail = new TView();
    var navigationPage = new NavigationPage(masterDetail);
    viewModel.SetNavigation(navigationPage.Navigation);
    masterDetail.BindingContext = viewModel;
    return navigationPage;
}

Now lets focus on base class for new control view model: MasterDetailControlViewModel. It is almost the same as in previous example, but it do not contain any logic that handles side menu functionality.

public class MasterDetailControlViewModel : INotifyPropertyChanged, INavigation
{
    private Page _detail;
    private INavigation _navigation;

    private Stack<Page> _pages = new Stack<Page>();

    public event PropertyChangedEventHandler PropertyChanged;

    public Page Detail
    {
        get { return _detail; }
        set
        {
            if (_detail != value)
            {
                _pages.Push(Detail);
                _detail = value;
                OnPropertyChanged();
            }
        }
    }

    public IReadOnlyList<Page> ModalStack { get { return _navigation.ModalStack; } }

    public IReadOnlyList<Page> NavigationStack
    {
        get
        {
            if (_pages.Count == 0)
            {
                return _navigation.NavigationStack;
            }
            var implPages = _navigation.NavigationStack;
            MasterDetailControl master = null;
            var beforeMaster = implPages.TakeWhile(d =>
            {
                master = d as MasterDetailControl;
                return master != null || d.GetType() == typeof(MasterDetailControl);
            }).ToList();
            beforeMaster.AddRange(_pages);
            beforeMaster.AddRange(implPages.Where(d => !beforeMaster.Contains(d)
                && d != master));
            return new ReadOnlyCollection<Page>(_navigation.NavigationStack.ToList());
        }
    }

    public void InsertPageBefore(Page page, Page before)
    {
        if (_pages.Contains(before))
        {
            var list = _pages.ToList();
            var indexOfBefore = list.IndexOf(before);
            list.Insert(indexOfBefore, page);
            _pages = new Stack<Page>(list);
        }
        else
        {
            _navigation.InsertPageBefore(page, before);
        }
    }

    public Task<Page> PopAsync()
    {
        Page page = null;
        if (_pages.Count > 0)
        {
            page = _pages.Pop();
            _detail = page;
            OnPropertyChanged("Detail");
        }
        return page != null ? Task.FromResult(page) : _navigation.PopAsync();
    }

    public Task<Page> PopAsync(bool animated)
    {
        Page page = null;
        if (_pages.Count > 0)
        {
            page = _pages.Pop();
            _detail = page;
            OnPropertyChanged("Detail");
        }
        return page != null ? Task.FromResult(page) : _navigation.PopAsync(animated);
    }

    public Task<Page> PopModalAsync()
    {
        return _navigation.PopModalAsync();
    }

    public Task<Page> PopModalAsync(bool animated)
    {
        return _navigation.PopModalAsync(animated);
    }

    public Task PopToRootAsync()
    {
        var firstPage = _navigation.NavigationStack[0];
        if (firstPage is MasterDetailControl
            || firstPage.GetType() == typeof(MasterDetailControl))
        {
            _pages = new Stack<Page>(new[] { _pages.FirstOrDefault() });
            return Task.FromResult(firstPage);
        }
        return _navigation.PopToRootAsync();
    }

    public Task PopToRootAsync(bool animated)
    {
        var firstPage = _navigation.NavigationStack[0];
        if (firstPage is MasterDetailControl
            || firstPage.GetType() == typeof(MasterDetailControl))
        {
            _pages = new Stack<Page>(new[] { _pages.FirstOrDefault() });
            return Task.FromResult(firstPage);
        }
        return _navigation.PopToRootAsync(animated);
    }

    public Task PushAsync(Page page)
    {
        Detail = page;
        return Task.FromResult(page);
    }

    public Task PushAsync(Page page, bool animated)
    {
        Detail = page;
        return Task.FromResult(page);
    }

    public Task PushModalAsync(Page page)
    {
        return _navigation.PushModalAsync(page);
    }

    public Task PushModalAsync(Page page, bool animated)
    {
        return _navigation.PushModalAsync(page, animated);
    }

    public void RemovePage(Page page)
    {
        if (_pages.Contains(page))
        {
            var list = _pages.ToList();
            list.Remove(page);
            _pages = new Stack<Page>(list);
        }
        _navigation.RemovePage(page);
    }

    public void SetNavigation(INavigation navigation)
    {
        _navigation = navigation;
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Anything that will involve siide menu will be in application specific code view model. Application will contains from the same detail views.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomMasterDetail2.DetailX">
  <Label Text="DetailX" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

App.xaml file is also the same and most of its code behind was shown above.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomMasterDetail2.App">
</Application>
using CustomMasterDetailControl;

namespace CustomMasterDetail2
{
    public partial class App
    {
        public App()
        {
            InitializeComponent();
            MainPage = MasterDetailControl.Create<MasterDetail, MasterDetailViewModel>();
        }
    }
}

MasterDetail.xaml file is application specific implementation of control and contains menu (the same as in previous example).

<?xml version="1.0" encoding="utf-8" ?>
<masterDetail:MasterDetailControl 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:masterDetail="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
    x:Class="CustomMasterDetail2.MasterDetail">
    <masterDetail:MasterDetailControl.SideContent>
        <StackLayout>
            <Button Text="Detail1" Command="{Binding ToDetail1}" />
            <Button Text="Detail2" Command="{Binding ToDetail2}" />
            <Button Text="Detail3" Command="{Binding ToDetail3}" />
        </StackLayout>
    </masterDetail:MasterDetailControl.SideContent>
</masterDetail:MasterDetailControl>

Menu logic is of course contained in view model, MasterDetailViewModel class.

public class MasterDetailViewModel : MasterDetailControlViewModel
{
    private ICommand _toDetail1;
    private ICommand _toDetail2;
    private ICommand _toDetail3;

    public ICommand ToDetail1
    {
        get
        {
            return _toDetail1 ?? (_toDetail1 = new Command(OnToDetail1));
        }
    }

    public ICommand ToDetail2
    {
        get
        {
            return _toDetail2 ?? (_toDetail2 = new Command(OnToDetail2));
        }
    }

    public ICommand ToDetail3
    {
        get
        {
            return _toDetail3 ?? (_toDetail3 = new Command(OnToDetail3));
        }
    }

    private void OnToDetail1()
    {
        Detail = new Detail1();
    }

    private void OnToDetail2()
    {
        Detail = new Detail2();
    }

    private void OnToDetail3()
    {
        Detail = new Detail3();
    }
}

After running this application effect will be the same as in previous one.

 

Best thing about this solution is great possibilities of configuration. In MasterDetailControl.xaml you can change anything you want: layout, widths, heights of elements, adds new bars on top or bottom, etc.

For example if we would want to hide menu on some detail pages, we can add new property to our control:

public bool SideContentVisible
{
    get { return Detail.SideContentVisible; }
}

Obviously for this to work there is need for above property SideContentVisible in detail page. Since our details are just ContentPage class instances we need to create new base class for details. Best way is to put it in PCL library along to our MasterDetailControl class.

public class DetailPage : ContentPage
{
    public DetailPage()
    {
        SideContentVisible = true;
    }

    public bool SideContentVisible { get; set; }
}

Obviously we need to change base classes of all details for this work. It is sometimes unnecessary or not desired, so let's change property in master detail control instead.

public bool IsMenuVisible
{
    get
    {
        var detailPage = Detail as DetailPage;
        if (detailPage != null)
        {
            return detailPage.SideContentVisible;
        }
        return true;
    }
}

This will handle ContentPage details to - menu visibility will just default to true. We can now create new detail called i.e. NoMenuDetail.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<masterDetail:DetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                         xmlns:masterDetail="clr-namespace:CustomMasterDetailControl;assembly=CustomMasterDetailControl"
                         x:Class="CustomMasterDetail2.NoMenuDetail"
                         SideContentVisible="False">
  <Label Text="No menu detail page" VerticalOptions="Center" HorizontalOptions="Center" />
</masterDetail:DetailPage>

We need only to add new menu item to application MasterDetail.xaml and handle this item command in view model.

<Button Text="NoMenuDetail" Command="{Binding ToNoMenuDetail}" />
private ICommand _toNoMenuDetail;

public ICommand ToNoMenuDetail
{
    get
    {
        return _toNoMenuDetail ?? (_toNoMenuDetail = new Command(OnToNoMenuDetail));
    }
}

private void OnToNoMenuDetail()
{
    Detail = new NoMenuDetail();
}

We need, of course, to create appropriate binding for this in MasterDetailControl.xaml and notify about property change, everytime Detail property changes.

<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomMasterDetailControl.MasterDetailControl"
             x:Name="Parent">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <StackLayout Grid.Column="0" x:Name="SideContentContainer"
                 WidthRequest="250"
                 IsVisible="{Binding SideContentVisible, Source={x:Reference Name=Parent}}" />
    <ContentView Grid.Column="1" x:Name="DetailContainer" />
  </Grid>
</ContentPage>

Binding is done by referencing control by its xaml name "Parent", which allows us to use it for binding source. It is impossible any other way in Xamaring without custom IMarkupExtension implementation (but I strongly encourage you to do that Smile). Another thing, that is worth to explain, is Auto width of side content Grid column. Without that change this column would take space even if its content would be invisible. Width of 250 is moved to WidthRequest property of StackLayout that serve as a container for menu.

Notification about SideContentVisible property change should be done every time detail changes and it involves only one line in DetailProperty declaration.

public readonly BindableProperty DetailProperty = BindableProperty.Create("Detail",
    typeof(ContentPage), typeof(MasterDetailControl),
    propertyChanged: (bindable, value, newValue) =>
    {
        var masterPage = (MasterDetailControl)bindable;
        masterPage.DetailContainer.Content = newValue != null ?
            ((ContentPage)newValue).Content : null;
        masterPage.OnPropertyChanged("SideContentVisible");
    });

After running changed code we will see on mobile device screen something like this:

 

After clicking on new menu item menu will disapper:

 

Summary

Above examples can serve as a base to create more specific controls thanks to great flexibility of this solution. It is defined by developer and it is tottally open in contrast to Xamarin native solution with it internal functionality, not accesible to end user of Xamarin. Since navigation really happens in view model it is much more viable for bigger applications that in most cases do not have parameterless constructors for view models and other classes. Base classes for control and its view model handles basic functionality, which can be extened very easily, so there is no need to do it every time in every single application. Control is created only once, which helps conserve memory. This would be impossible if every single page had the same menu. Oh, and it handles back button well Smile.

Sample applications with code examples used in this article you can find here: XamarinSamples.zip (94.48 kb)

Also there is available public github repository with code for this article and previous ones from Xamarin series.

Some features might still be missing or few things might be handled better. For example navigation from view model could be done by creating view models instead of views. This is better pattern and it is much simpler since navigation requires creating only one instance of view model instead of view and view model (at least in final application logic code). This would need some mechanism for finding out proper view for view model and/or registration mechanism for those pairs. Also it would be much easier to have the same navigation for detail pages and simple, whole screen content pages. For this detail pages can identified by base class DetailPage. This would create nice and simple navigation for every page via just view models regardless of specifics of a layout of views (which should not be connected to application logic anyway).

I will cover those topics in next article.

 



Xamarin Style based on implicit Style

clock April 22, 2016 19:17 by author n.podbielski

Next article about bugs in Xamarin will touch topic of Styles. Styles for all of controls not only Frame (and if you wondering why Frame you should check first two articles about Xamarin here and here).
First of all if you are using Styles in Xamarin you probably know how to define implicit styles. If not here is a sample:

<Application.Resources>
  <ResourceDictionary>
    <Style TargetType="Label">
      <Setter Property="FontSize" Value="45" />
    </Style>
  </ResourceDictionary>
</Application.Resources>

Almost the same as in WPF. If you will, or already did, use those kind of styles extensively you probably encountered problem with inheritance from them. How to do it? First consider this xaml:

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StylesInheritance.App">
  <Application.Resources>
    <ResourceDictionary>
      <Style TargetType="Label">
        <Setter Property="FontSize" Value="45" />
      </Style>
    </ResourceDictionary>
  </Application.Resources>
  <Application.MainPage>
    <ContentPage>
      <Grid>
        <Grid.Resources>
          <ResourceDictionary>
            <Style TargetType="Label">
              <Setter Property="BackgroundColor" Value="Red"></Setter>
            </Style>
          </ResourceDictionary>
        </Grid.Resources>
        <Label Text="Label Text" />
      </Grid>
    </ContentPage>
  </Application.MainPage>
</Application>

You have global implicit style for Label and want, locally for Grid, add red background too. According to this Xamarin documentation it should be possible right? Style have BaseResourceKey property, which won't be helpful since implicit styles do not have keys (actually they do but we will get back to this), but there is also property BasedOn. If you have Resharper you can even taste how this should work with Intellisense:

Should work, but it do not. Ok. Resharper inserts what it looks like a correct Xaml:

<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

 But after application starts we have (un)welcoming error:

 

Why this resource is not found? It is there, right? Notice strange key value, we will get back to that.

Maybe you will (or already did tried) to use Key property of StaticResourceExtension?

<Style TargetType="Label" BasedOn="{StaticResource Key={x:Type Label}}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

You will see another error:

 

That was something to be expected really since Key property is a string.

If you are so distrustful as me, maybe you tried to actually inspect Application resources to check if they are really there:

 

Implicit style is there oh right. Why Framework cannot find something that put in there itself? If you are perceptive you probably noticed that Keys is collection of strings and implicit styles actually do have keys, which are FullName of target types. We will have to do figure out how to set appropriate key for this type of styles. The easiest way is:

<Style TargetType="Label" BasedOn="{StaticResource Xamarin.Forms.Label}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

<Style TargetType="Label" BasedOn="{StaticResource Key=Xamarin.Forms.Label}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

The problem with first solution is that Resharper marks it as invalid (yeah this is really weak problem, but I like to my code to be clean that way) and second is not even checked for validity - you can put there anything - so it is even worse since you have only runtime errors. Of cource you can also assign proper key to implicit styles and make them explicit, and then assign this key to all controls, and then assign this key to child styles, and... well... this is to much work Wink.

On the side, I really do not get why Xamarin Team decided to make Resources keys a string and not objects like in WPF. If this would be the case, second Style declaration would work just fine.

{StaticResource Key={x:Type Label}}

In my opinion this is first bug/improvement they should make.

For fixing this issue I decided to write my own StaticResourceExtension, that will correctly calculate resources keys. Unfortunately as almost everything that could be useful to override in Xamarin is sealed (see Binding i.e.; not sealed in WPF). Because of that we have to do this this way:

[ContentProperty("KeyOrType")]
public class StaticResourceExtExtension : IMarkupExtension
{
    private readonly StaticResourceExtension _xamarinStaticExtension;

    public StaticResourceExtExtension()
    {
        _xamarinStaticExtension = new StaticResourceExtension();
    }

    public object KeyOrType { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        var type = KeyOrType as Type;
        if (type != null)
        {
            _xamarinStaticExtension.Key = type.FullName;
        }
        else
        {
            var s = KeyOrType as string;
            if (s != null)
            {
                _xamarinStaticExtension.Key = s;
            }
        }
        return _xamarinStaticExtension.ProvideValue(serviceProvider);
    }
}

This will work only in newer versions of Xamarin. For example in 1.3.3.6323 this class is internal (which is interesting how it is available in Xaml). In earlier versions you have resolve to reflection or write your own mechanism for obtaining correct resources (it is not really that complicated).

With this you declare your style like this:

<Style TargetType="Label" BasedOn="{stylesInheritance:StaticResourceExt {x:Type Label}}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

Which will still throw and error Undecided, which is strange to me and it is second bug involving styles inheritance.

 

It is the same error as in the first try with Xamarin StaticResourceExtension. If you will debug this code you can check for KeyOrType property value:

This is the point when I just stopped amazed what I found. Laughing

Really, I expected a bug, but not like this one. If you will look closely on first screen with error, you will notice that the same thing happens in Xamarin extension (and why I think that using Xamarin is like run through minefield). Apparently Xaml parser do not expect here another extension and it threats everything as plain string instead of evaluating it and then putting it in StaticResourceExtension. However this is only one thing. Why there is no closing bracket '}'? Maybe first problem originates from second one and parser threats unclosed brackets as just string and not correctly as Xaml declaration. If we add explicitly KeyOrType property to declaration it will be resolved by parser just right.

<Style TargetType="Label" BasedOn="{stylesInheritance:StaticResourceExt KeyOrType={x:Type Label}}">
  <Setter Property="BackgroundColor" Value="Red"></Setter>
</Style>

But we did not got so far without doing it (almost) perfect! Good thing we can change how our extension behaves, in contrast to Xamarin one. We have only to resolve type from its xaml abbreviation. Luckily IServiceProvider have access for type just for that: IXamlTypeResolver. With method of this type:

Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider = null);

we can easily obain correct type.

public object ProvideValue(IServiceProvider serviceProvider)
{
    var type = KeyOrType as Type;
    if (type != null)
    {
        _xamarinStaticExtension.Key = type.FullName;
    }
    else
    {
        var s = KeyOrType as string;
        if (s != null)
        {
            const string xType = "{x:Type ";
            if (s.StartsWith(xType))
            {
                var typeName = s.Replace(xType, "");
                var xamlTypeResolver = (IXamlTypeResolver)serviceProvider.GetService(typeof(IXamlTypeResolver));
                _xamarinStaticExtension.Key = xamlTypeResolver.Resolve(typeName, serviceProvider).FullName;
            }
            else
            {
                _xamarinStaticExtension.Key = s;
            }
        }
    }
    return _xamarinStaticExtension.ProvideValue(serviceProvider);
}

And it will work. As we wanted we will see big font label on red background.

 

Of course instead of removing "{x:Type " part of string it is possible to do this:

public object ProvideValue(IServiceProvider serviceProvider)
{
    var type = KeyOrType as Type;
    if (type != null)
    {
        _xamarinStaticExtension.Key = type.FullName;
    }
    else
    {
        var s = KeyOrType as string;
        if (s != null)
        {
            const string bracket = "{";
            if (s.StartsWith(bracket))
            {
                var xamlTypeResolver = (IXamlTypeResolver)serviceProvider.GetService(typeof(IXamlTypeResolver));
                var extensionName = s.Replace(bracket, "").Split(' ')[0];
                var extension = (TypeExtension)Activator.CreateInstance(xamlTypeResolver.Resolve(extensionName, serviceProvider));
                extension.TypeName = s.Split(' ')[1];
                var typeName = extension.ProvideValue(serviceProvider).FullName;
                _xamarinStaticExtension.Key = typeName;
            }
            else
            {
                _xamarinStaticExtension.Key = s;
            }
        }
    }
    return _xamarinStaticExtension.ProvideValue(serviceProvider);
}

But I think there is no point in that. This code is much less clear and also not 100% percent bulletproof. I think it is worth to do only if you planning to add your own extension for type resolving, because styles added to resources either have Type.FullName key or just some arbitrary name as key. Previous solution is then sufficient. Below is sample application.

 StylesInheritance.zip (54.13 kb)

Also there is available public github repository with code for this article and other ones from Xamarin series.



Xamarin Frame and disappearing Outline

clock April 22, 2016 09:00 by author n.podbielski

This is another article in mini series of Xamarin bugs and workarounds for them Smile. Previous one you can find here.

This one is again about Frame control. I promise that another one will be about something else Cool.

Issue can be observed when you bind BackgroundColor property of Frame control and something will trigger change of view model source property binded to background color.

But since one image is more than thousand words - here is gif with this bug.

 

Frame is quite big, but if you look near corners of it, you will see, that there are round when background is green and when background change to Red, they disappear. Outline disappear too. It is slight white line around green field (it is even less visible), but I assure you that it is there. Smile

Ok here is a code for this example in xaml App file:

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="FrameBug.App">
  <Application.MainPage>
    <ContentPage>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"></RowDefinition>
          <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <StackLayout Padding="10">
          <Frame VerticalOptions="Center" OutlineColor="White" 
                 BackgroundColor="{Binding LineColor}">
            <Label Text="Label" FontSize="60" />
          </Frame>
        </StackLayout>
        <Button Text="Change color" Grid.Row="1" Command="{Binding ChangeColor}"></Button>
      </Grid>
    </ContentPage>
  </Application.MainPage>
</Application>

One noticable thing done in code behind for this xaml file is adding BindingContext instance (App.xaml.cs file):

namespace FrameBug
{
    public partial class App
    {
        public App()
        {
            InitializeComponent();
            BindingContext = new AppViewModel();
        }
    }
}

Ok. One missing piece is AppViewModel class:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;

namespace FrameBug
{
    public class AppViewModel : INotifyPropertyChanged
    {
        private ICommand _changeColor;

        public event PropertyChangedEventHandler PropertyChanged;

        public ICommand ChangeColor => _changeColor ?? (_changeColor = new Command(OnChangeColor));

        public Color LineColor { get; set; } = Color.Green;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void OnChangeColor()
        {
            LineColor = Color.Red;
            OnPropertyChanged("LineColor");
        }
    }
}

As you may notice it is C# code from VS2015. It will not work in prior versions, but there is not really point in doing test Xamarin apps in earlier versions since Xamarin recently was released for free even in VS2015 community edition.

With this simple application added to Xamarin template solution we can reproduce bug as in above gif. But how to fix it? And why it occurs? It took some digging and Resharper help with decompiling FrameRenderer to find out why. Yes, this class that renders Frame control as plain bitmap in Android environment is source of the problem. This class (since there is no public source code I will not include disassembled source code) have private UpdateBackground method which creates custom class based on Android Drawable class. It in its bowels it renders Background with solid color from BackgroundColor property of Frame control and Outline as a line (or rather rounded rectangle) from OutlineColor property of Frame. Those methods suppose to work either on initialization or BackgroundColor, OutlineColor properties change according to code of mentioned class. All good, right? When I created very similar class, attached it to custom renderer of Frame both operations was performed correctly during debugging session. But still frame was drawn incorrectly. It took some experimentation with height and width of rectangles in bitmap and it struck me that nothing happens even with 1x1 px size, because background is rerendered in some other place. Where? Since no more code in renderer was touching drawing it had to be in base class - VisualElementRenderer<>. If you disassemble it too, you will see that it have SetBackgroundColor and it was my first suspect. This method is executed on property change of control. Which one you ask? Smile BackgroundColor property. I did not dig any further since it is virtual method. Quick test proved my suspect guilty. Adding following override method to custom renderer fixed this problem.

public override void SetBackgroundColor(Color color)
{

}

Since code in FrameRenderer and its base class was executing correctly it probably was executing in wrong order. Probably it was missed by Xamarin Team after fixing this bug and closing this one. Or maybe this method as virtual was added after fix and tests and testers missed it? Nevertheless, writing new custom renderer based on Xamarin one fix this issue:

[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
namespace FrameBug.Droid
{
    public class FrameRenderer : Xamarin.Forms.Platform.Android.FrameRenderer
    {
        public override void SetBackgroundColor(Color color)
        {
            //base.SetBackgroundColor(color);
        }
    }
}

Commented line can be used to trigger the buggy behavior if anyone is curious enough Smile.

This is another one of mine unpleasant adventures with Xamarin framework. I hope this can be helpful and spare you some time. You can find sample application below.

FrameBug.zip (52.56 kb)

Also there is available public github repository with code for this article and other ones from Xamarin series.



Xamarin Frame and Style with Padding

clock April 22, 2016 01:10 by author n.podbielski

If you are a bit advanced with Xamarin you should already now that there is possibility of styling your controls almost like in WPF.
Almost, because there is a few strange bugs involving this. For example there is a problem with styling a Padding property in Frame. Consider following Xaml of Xamarin application (I tested it in Android):

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styles="clr-namespace:Styles;assembly=Styles"
             x:Class="Styles.App">
  <Application.Resources>
    <ResourceDictionary>
      <Style TargetType="Frame" x:Key="Style">
        <Setter Property="Padding" Value="0" />
      </Style>
    </ResourceDictionary>
  </Application.Resources>
  <Application.MainPage>
    <ContentPage>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="100" />
            <RowDefinition Height="100" />
            <RowDefinition Height="100" />
          <RowDefinition Height="100" />
        </Grid.RowDefinitions>
        <Frame VerticalOptions="Center"
               Grid.Row="0"
               Padding="0">
          <Label Text="100 height" FontSize="60" />
        </Frame>
        <Frame VerticalOptions="Center" Style="{StaticResource Style}"
               Grid.Row="1"
               Padding="0">
          <Label Text="100 height" FontSize="60" />
        </Frame>
        <Frame VerticalOptions="Center" Style="{StaticResource Style}"
               Grid.Row="2">
          <Label Text="100 height" FontSize="60" />
        </Frame>
      </Grid>
    </ContentPage>
  </Application.MainPage>
</Application>

It almost the same as default (created by template) shared application - but instead of App.cs we have more advanced Xaml file.

What not suspecting (or better: excepting a normal behavior) user would except all Frames to have the same view because properties will have the same values. Should have the same values, but they are not. This code will produce following layout:

 

Last Frame do not have explicit setting for Padding in its declaration and this property is resolved to default value and its contents just do not fit in available space. Why? There is a bug for that.

But according to comments Xamarin Team is not exactly willing to fix it.

Fortunately there is a quick workaround for that. The easiest thing to do is to just add this method to new class named i.e. FixedFrame:

public class ExtendedFrame : Frame
{
    protected override void OnPropertyChanged(string propertyName = null)
    {
        // ReSharper disable once ExplicitCallerInfoArgument
        base.OnPropertyChanged(propertyName);
        if (propertyName == StyleProperty.PropertyName)
        {
            if (Style.Setters.Any(s => s.Property == PaddingProperty))
            {
                Padding = (Thickness)Style.Setters.First(s => s.Property == PaddingProperty).Value;
            }
        }
    }
}

Using this class instead of original Frame from Xamarin framework will ensure proper Padding value after applying Style to FixedFrame control. Consider changed Android application from above, with added new FixedFrame control at the end. Only property set is Style and theer is no explicit value for Padding in control declaration:

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styles="clr-namespace:Styles;assembly=Styles"
             x:Class="Styles.App">
  <Application.Resources>
    <ResourceDictionary>
      <Style TargetType="Frame" x:Key="Style">
        <Setter Property="Padding" Value="0" />
      </Style>
    </ResourceDictionary>
  </Application.Resources>
  <Application.MainPage>
    <ContentPage>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="100" />
            <RowDefinition Height="100" />
            <RowDefinition Height="100" />
          <RowDefinition Height="100" />
        </Grid.RowDefinitions>
        <Frame VerticalOptions="Center"
               Grid.Row="0"
               Padding="0">
          <Label Text="100 height" FontSize="60" />
        </Frame>
        <Frame VerticalOptions="Center" Style="{StaticResource Style}"
               Grid.Row="1"
               Padding="0">
          <Label Text="100 height" FontSize="60" />
        </Frame>
        <Frame VerticalOptions="Center" Style="{StaticResource Style}"
               Grid.Row="2">
          <Label Text="100 height" FontSize="60" />
        </Frame>
        <styles:FrameExt VerticalOptions="Center" Style="{StaticResource Style}"
               Grid.Row="3">
          <Label Text="100 height" FontSize="60" />
        </styles:FrameExt>
      </Grid>
    </ContentPage>
  </Application.MainPage>
</Application>

As a result we will have following window layout:

 

As you see last row with FixedFrame control have proper Padding property value. Smile
Simple and effective. Below sample application.

Styles.zip (324.08 kb)

Also there is available public github repository with code for this article and other ones from Xamarin series.



Safe cast string to enum

clock February 25, 2014 18:49 by author n.podbielski

In on of my projects I had make an interface between database and web service in C# code.

One of problem I had to face was need to cast strings to enumerable types, because of simple fact that database have no idea what is an 'enum'. Yes simplest mapping between an enumeration and database type is integer. It is simplest but in my idea not best. For example I really do not remember (or do not WANT to remember) what is the meaning of 4 integer value in case of package shipment status.

We can have values like this:

0 - RequestReceived
1 - Accepted
2 - Assembling
3 - Sended
4 - CustomerReceived
5 - Completed

Let's assume that is valid flow of package delivery flow. For someone that is working on system that handles monitoring of that kind of mechanism, this could be not an issue, because this flow is easy to recall. Yes. But as company grows, software grows. And most likely more enumeration types will be created.

So to have best of to worlds, in my opinion this is best to have enumeration in strings and with possibility to cast strings like '0' and 'RequestReceived' to RequestReceived enum value.

Nice feature is to make casting also case insensitive. But this is not necessary.

Aside from interfacing with database there are other use case that come to mind:

1. User interfaces input

2. Type serialization to JSON and from JSON

3. XML serialization

4. Import from various data sources like CSV

Ok. That is for introduction. Let's go to the coding.

First we have to retrieve values of enum type:

var values = Enum.GetValues(typeof(TEnum));

This is simple. Static method of Enum special class returns all possible values of an enum. With that knowledge we can just use foreach loop to iterate through collection:

public static EnumTest GetEnumValue(string val)
{
    var values = Enum.GetValues(typeof(EnumTest));
    foreach (EnumTest value in values)
    {
        if (val.Equals(value.ToString()))
        {
            return value;
        }
    }
    return 0;
}

There is a few problems with this method. For first: we can use it with only one predefined type of enum. Sadly this is impossible to create generic enum-only method in C#. But we can do pretty close. Second problem is that we don't have too have default value of enum type with integer equivalent of 0. 0 can not be in an enum at all!
 For first issue we can add generic type argument with constraints of interfaces implemented by enum types and with struct special constraint.
 For second issue we can use default C# keyword. Our 'better' method will be declared as following:

public static TEnum GetEnumValue(string val)
    where TEnum : struct, IComparable, IFormattable, IConvertible
{
    var values = Enum.GetValues(typeof(TEnum));
    foreach (TEnum value in values)
    {
        if (val.Equals(value.ToString()))
        {
            return value;
        }
    }
    return default(TEnum);
}

Of course there are can be other types that can work with this method but are in fact not an enumerable types, but this is best solution available in C# (that I know of). Default statement in case where string value is not found in method will return first defined value of an enum.

Next step will be add possibility of integers values in strings. For that we have to cast enum values to type int. We cannot do that with simple casting operation in C#, because it is not possible with generic type we defined in our improved method. But we can use IConvertible interface and its ToInt32 method. It requires format provider for casting though. I used CultureInfo.CurrentCulture property which was OK in my application, but this could be a problem in others. It depends where it will be used. Changed method will look like this:

 

public static TEnum GetEnumValue2(string val)
    where TEnum : struct, IComparable, IFormattable, IConvertible
{
    var values = Enum.GetValues(typeof(TEnum));
    foreach (TEnum value in values)
    {
        if (val.Equals(value.ToString())
            || val == (value.ToInt32(CultureInfo.CurrentCulture)).ToString())
        {
            return value;
        }
    }
    return default(TEnum);
}

This mostly work ok, but this might be a problem when it is used like this:

package.Status = GetEnumValue<PackageStatus>(newStatusString);

Why? Because when newStatusString value is not proper value for and enum, status property will reset to default status value. It might be a problem. Solution might be exception throwing when value is invalid. This would be good for UI. I decided to use custom default value:

package.Status = GetEnumValue(newStatusString, package.Status);

This way status will not change if value in string is inalid and old value will be assigned.

Finally I added case insensitivity for string comparison. There are plenty of possibilities to do that in .NET so this is something that should be considered in regards of application which will be using that code. For example we can do something like this:

public static TEnum GetEnumValue2(string val,TEnum current)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
    var values = Enum.GetValues(typeof(TEnum));
    foreach (TEnum value in values)
    {
        if (val.Equals(value.ToString(), StringComparison.OrdinalIgnoreCase)
            || val == (value.ToInt32(CultureInfo.CurrentCulture)).ToString())
        {
            return value;
        }
    }
    return current;
}

Nice have feature is to defined this method as extension method for string. This way we can call it after writing name of the variable with our string value.

package.Status = newStatusString.GetEnumValue(package.Status);

I prefer to do this that way, because it is more expressive to my coding style. While writing solution for some kind of a problem I think: I want here this value but after mapping it in the following way. With using method GetEnumValue as a plain method not extension, it is in my opinion greater burden for someone who read code (which is mostly me and I always want to make my life easier :) ). But this is subject of another article.

Anyway this can be achieved just by adding this keyword and placing method in separate class.

public static class Extension
{
    public static TEnum GetEnumValue( this string val, TEnum current)
where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        var values = Enum.GetValues(typeof(TEnum));
        foreach (TEnum value in values)
        {
            if (val.Equals(value.ToString(), StringComparison.OrdinalIgnoreCase)
                || val == (value.ToInt32(CultureInfo.CurrentCulture)).ToString())
            {
                return value;
            }
        }
        return current;
    }
}

This is very simple solution for this particular problem. There are more thing that can be changed/improved. You can download code sample and play with it yourself :)

Enjoy!

 Program.cs (3.55 kb)



Visual Studio code snippets

clock February 25, 2014 07:39 by author n.podbielski

What I really like in Visual Studio as development environment is code snippets. Well actually probably other IDE have something very similar but I do not have much of a choice in that matter as C# developer :)
Anyway this feature is really useful. For example while refactoring I very often use if surround snippet.

Unfortunately there are no easy way to edit/add new snippets since VS do not have built in snippets editor. But their syntax is almost XML and quite simple. I will cover few simple examples in this short article.

Ok. Let's start with what we have in VS. There is Code Snippets Manager available Tool menu.

 

There is not much we can do in that window: add or remove snippets directory or import single snippets. In my opinion only real useful feature is possibility to check where we can find existing snippets. Also I do not recommend to add new folders because of extra need to specify search directory when we search for desired snippet after using insert snippet Visual Studio shortcut (Ctrl K, X by default for Visual C# environment setting). For example when selecting for surround statement in Java Script; after adding new folder we have to choose folder before we choose snippet, which is waste of time for me.

 Ok. Let's try a simple example. While developing in Java Script it is good idea to use Require JS or other AMD library.
 It would be really painful to write every time same formula for new JS module. Instead it is better to create code snippet. Using Code Snippets Manager we can obtain directory in which JS snippets resides. By default this is: C:\Program Files (x86)\Microsoft Visual Studio 11.0\JavaScript\Snippets\1033\JavaScript for VS 2012.

Desired code for starting a new module for me was:

define([''],
    function (dep) {	
        return new function () {
			//private variables
			//private functions
			//public functions
			$selected$$end$
			//globals
			//onload
		};
    });

To define code snippet with above template we have to create new file {name}.snippet in above directory.

File contents should be similar to following:

  
<CodeSnippet Format="1.1.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>module</Title>
    <Author>Natan Podbielski</Author>
    <Shortcut>mdl</Shortcut>
    <Description>Code snippet Require JS module</Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="JavaScript"><![CDATA[
define([''],
    function (dep) {	
        return new function () {
			//private variables
			//private functions
			//public functions
			$selected$$end$
			//globals
			//onload
		};
    });
]]></Code>
  </Snippet>
</CodeSnippet>

Keep in mind that white space in <Code> tag are very meaningful and should look exactly like it should look in destination code in VS. With file like this you can use it in VS. Press insert snippet shortcut in JS file. You should see something like this (sometimes it is necessary to open Code Snippets Manager or restart VS to force to reread all snippets):

 

 

 

Quick explanation about tags:

<Title> is a title that is shown in Code Snippet Manager and 'select snippet menu'.

<Author> is an author of the snipper. Visible in Code Snippet Manager.

<Shortcut> is a string of characters you have to type which make available to you Tab shortcut which will expand typed characters into desired code from snippet. Like ctor is a shortcut for built in VS constructor snippet.

<Description> is a description visible from Code Snippet Manager and from 'select snippet menu'.

<SnippetTypes> is an enumeration of snippet type. There is only two types of snippets: Expansion and SurroundsWith. With <SnippetType> tag you can decide which is it and from what menu or shortcut it will be available.

<Code> is an actual code for snippet. Should be 1 to 1 to desired code. Besides snippets variables which I will describe bellow.

 

You should be very specific about white space, indentation of code of snippet (about syntax too of course but it is obvious) etc. It is better to write it right once in snippet than correct it every time you use it :)

There are available plenty of variables inside of snippet. They are defined like $name$ inside actual code.

Most important for example for surround type of snippets is variable $selected$, which describe where VS should put text that you selected before inserting desired template. $end$ specifies when cursor should be after inserting snippet. It is useful for snippets that you should populate with specific code, method for example: you should write method body so this is useful to put $end$ inside method body. There are some variables defined by snippet engine, like $classname$, which is current class name. You can also defines your own variables, which then can be easily changed while using snippet. Consider built in snippet for property with backing field: propfull.

It has declaration as follows:

		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>Property type</ToolTip>
					<Default>int</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
				<Literal>
					<ID>field</ID>
					<ToolTip>The variable backing this property</ToolTip>
					<Default>myVar</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[private $type$ $field$;

	public $type$ $property$
	{
		get { return $field$;}
		set { $field$ = value;}
	}
	$end$]]>
			</Code>
		</Snippet>

Declaration of variables $type$ and $field$ allow us to write name of field and type of field and property one time in one place:

 

Changing myVar for something more meaningful and submitting a snippet will change highlighted places with myVar value inside snippet code too. Pretty useful!

As you can see in actual declaration of snippet variables there is possibility of setting default value and tool tip so and end user could know what we meant for variable while creating a template. There is a really world of possibilities from such snippets. If we using some kind of elaborate code structures inside of our project (custom data contracts for example, or ASP.NET custom controls etc.) it is best to create snippets for them. Or for other broadly used code samples.

For example I created snippet for jQuery ready function:

 <CodeSnippet Format="1.1.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>jQuery ready</Title>
    <Author>Natan Podbielski</Author>
    <Shortcut>docrd</Shortcut>
    <Description>Code snippet for jQuery document ready event</Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="JavaScript"><![CDATA[$$(function(){
	$selected$$end$
});]]></Code>
  </Snippet>
</CodeSnippet>

Important: as you can see above to achieve $ inside destination code (after using a snippet inside VS) you have to escape it with $, so jQuery $ becomes $$ inside snippet code declaration!

Or for console.log(); JS function, which is very similar but with <Code> tag like this:

 <Code Language="JavaScript"><![CDATA[console.log($end$);]]></Code>

I hope this will help in your journey into VS code snippets world. :)



Smart string builder

clock February 21, 2014 23:16 by author n.podbielski

In this rare times when I was writing code that suppose to create large string from smaller chunks and with some non string parameters I was using StringBuilder class of course. Of course because string adding, or string concatenation is very costly when talking about memory utilization. It is because every time you do this:

var text = "Hello world!"+ "\n"+ "How are you?";

new string is created for every '+' operation in memory. Not a best way of doing strings creations. StringBuilder is better because it do not creates strings until you call .ToString method of StringBuilder class.

So instead of doing something like was shown above code should look something like follows:

var stringBuilder = new StringBuilder();
stringBuilder.Append("Hello world!");
stringBuilder.Append("\n");
stringBuilder.Append("How are you?");
Console.WriteLine(stringBuilder);

This is better but while writing this code I disliked of how much you have to type to just add another string to string builder object. I thought: can this be done better? In fact it can. And this is way I created SmartStringBuilder class.

Requirement was to have something like this:

var smartBuilder = new SmartStringBuilder();
smartBuilder += "Hello World!";
smartBuilder += "\n";
smartBuilder += "How are you?";
Console.WriteLine(smartBuilder);

Luckily C# alows to write custom behaviors for operators like '+'. To do this we can use special operator keyword in C#. For managing chunks of strings we will use private instance of StringBuilder class:

public class SmartStringBuilder
{
    private StringBuilder internalStringBuilder = new StringBuilder();

    public SmartStringBuilder() { }

    public SmartStringBuilder(string str)
    {
        internalStringBuilder.Append(str);
    }

    public override string ToString()
    {
        return internalStringBuilder.ToString();
    }

    public static SmartStringBuilder operator +(SmartStringBuilder smartBuilder, string addString)
    {
        smartBuilder.internalStringBuilder.Append(addString);
        return smartBuilder;
    }
}

This allows us to execute first 3 lines of 'requirements code'

var smartBuilder = new SmartStringBuilder();
smartBuilder += "Hello World!";
smartBuilder += "\n";
smartBuilder += "How are you?";

To allows using our new class us string in Console.WriteLine method (or any other method that takes string parameter) we need to add implicit casting operator to string type:

public static implicit operator string(SmartStringBuilder smartBuilder)
{
   return smartBuilder.ToString();
}

With our class defined like this we can execute following line:

Console.WriteLine(smartBuilder);

Another nice feature is possibility of adding values of other types to our builder with + sign. We can do this by adding + operator to our class for each of them. For example for int type this method would look like this:

public static SmartStringBuilder operator +(SmartStringBuilder smartBuilder, int number)
{
    smartBuilder.internalStringBuilder.Append(number);
    return smartBuilder;
}

This allows us to execute following line safely:

smartBuilder += 1;

Ideally would be to add possibility of formatting values of other types with format strings i.e.:

smartBuilder += ("format {0}", 1);

But this is impossible without changing C# language itslelf. Best thing we can do is to add AppendFormat method that executes method with the same name in internal StringBuilder object.

public void AppendFormat(string format, params object[] parameters)
{
     internalStringBuilder.AppendFormat(format, parameters);
}

Our whole class will look like this:

public class SmartStringBuilder
{

    private StringBuilder internalStringBuilder = new StringBuilder();

    public SmartStringBuilder() { }

    public SmartStringBuilder(string str)
    {
        internalStringBuilder.Append(str);
    }

    public override string ToString()
    {
        return internalStringBuilder.ToString();
    }

    public static implicit operator string(SmartStringBuilder smartBuilder)
    {
        return smartBuilder.ToString();
    }

    public static SmartStringBuilder operator +(SmartStringBuilder smartBuilder, string addString)
    {
        smartBuilder.internalStringBuilder.Append(addString);
        return smartBuilder;
    }

    public static SmartStringBuilder operator +(SmartStringBuilder smartBuilder, int number)
    {
        smartBuilder.internalStringBuilder.Append(number);
        return smartBuilder;
    }

    public void AppendFormat(string format, params object[] parameters)
    {
        internalStringBuilder.AppendFormat(format, parameters);
    }
}

That is it. Not much of magic but it simplifies string manipulation a little Smile.



Expression parsing and nested properties

clock February 21, 2014 23:00 by author n.podbielski

In one of projects I was working on, I needed to get property value from property path and property path from expression.

First, lets cover second case.

With expression in form of nested property value:

()=>object1.object2.object3.object4

we cannot take simple value of some property, because we have only root object, object1.

Instead we have to take every value of every object in the middle and last object as value od our desired property.

If we would have expression like this:

()=>object1.object2

and value of root object (object1), we can just cast expression above to MemberExpression type and then retrieve name of the property, from property MemberExpression.Member.Name.

But if we need another property of object3 or even deeper, we need to retrieve another, nested MemberExpression from expression above.

Without knowledge of depth of our expression, we had to repeat that operation as long as property MemberExpression.Expression has value different than null.

To take MemberExpression from given Expression (which can be of many types: MemberExpression, LambdaExpression, UnaryExpression) we can use following method:

public static MemberExpression GetMemberExpression(Expression expression)
{
    if (expression is MemberExpression)
    {
        return (MemberExpression)expression;
    }
    else if (expression is LambdaExpression)
    {
        var lambdaExpression = expression as LambdaExpression;
        if (lambdaExpression.Body is MemberExpression)
        {
            return (MemberExpression)lambdaExpression.Body;
        }
        else if (lambdaExpression.Body is UnaryExpression)
        {
            return ((MemberExpression)((UnaryExpression)lambdaExpression.Body).Operand);
        }
    }
    return null;
}

 

This method will return MemberExpression from any of above types.

Armed with method like this we can write loop to retrieve property name for all levels of expression. For example we can use rarely used do...while loop. Of course we can use while loop, but this way we can have additional learning experience, using less known language constructs :)

 

public static string GetPropertyPath(Expression expr)
{
    var path = new StringBuilder();
    MemberExpression memberExpression = GetMemberExpression(expr);
    do
    {
        if (path.Length > 0)
        {
            path.Insert(0, ".");
        }
        path.Insert(0, memberExpression.Member.Name);
        memberExpression = GetMemberExpression(memberExpression.Expression);
    }
    while (memberExpression != null);
    return path.ToString();
}

In my code I placed those two methods in one class called ExpressionOperator and then used them in extension for type Object:

public static string GetPropertyPath<TObj, TRet>(this TObj obj, Expression<Func<TObj, TRet>> expr)
{
    return ExpressionOperator.GetPropertyPath(expr);
}

which can be used like this:

object1.GetPropertyPath(o =>o.object2.object3.object4)

which should return "object2.object3.object4" string. With possibility for returning a property path from any expression, we can now write method that returns value of destination property (last in the expression).

Method like this is even simpler than for returning property path. We just need to find value of every property in the middle of path to the point, when we have our destination property. For that we can use while loop :)

public static object GetPropertyValue(this object obj, string propertyPath)
{
    object propertyValue = null;
    if (propertyPath.IndexOf(".") < 0)
    {
        var objType = obj.GetType();
        propertyValue = objType.GetProperty(propertyPath).GetValue(obj, null);
        return propertyValue;
    }
    var properties = propertyPath.Split('.').ToList();
    var midPropertyValue = obj;
    while (properties.Count > 0)
    {
        var propertyName = properties.First();
        properties.Remove(propertyName);
        propertyValue = midPropertyValue.GetPropertyValue(propertyName);
        midPropertyValue = propertyValue;
    }
    return propertyValue;
}

Above code returns value of property by reflection. Property name is taken by splitting property path into parts separated by '.'. For example we can use this method in a following way:

object1.GetPropertyValue(o=>o.object2.object3.object4);

This is really simple example. For better usability you should add validating for root object (if this object have value in the first place) and for any of mid-objects. They did not have to have value too, they could not have value because of lack of initialization of object tree. Also good idea is to add boolean flag, if we want method to return an error in above cases or false (lack of success) and null value (value of desired property).

Another way to improve things is to add similar way to retrieve type of nested property or set property value by property path given as string.

In project I was working on I used this mechanism to retrieve properties path in C# code, transport it to client (it was web application so client was a browser) and set property value in JavaScript object, which had the same object tree. In other way I transported value of changed property at the client side and its path, to apply changes at server side. Very useful to synchronize two different data schemes.

Attached to this article is example console application for retrieving property path and property value of a nested property.

 Program.cs (3.78 kb)

I hope this will help :)