Monday, 8 July 2013

Custom Markup Extensions & Type Converters

There are times when we need to extend the xaml to support some special features for our application. A very simple example is like We have a textbox which takes firstname and lastname and displays it in the format 'LastName, FirstName', We can obviously format the string, but just to demonstrate what Markup extensions can do:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;

namespace WpfApplication3
{
    public class MyMarkupExtension:MarkupExtension
    {
        [ConstructorArgument("FirstName")]
        public string FirstName { get; set; }

        [ConstructorArgument("LastName")]
        public string LastName { get; set; }


        public MyMarkupExtension()
        {

        }

        public MyMarkupExtension(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }



        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return LastName + ", " + FirstName;
        }
    }
}



and in the xaml we write:-

<TextBlock Text="{local:MyMarkup FirstName=Shivakant, LastName=Upadhyay}"/>   


The result is visible even in the design time environment:-




Now for type converters, suppose you want to bind a special type to your program, for which there is no built in sonverter. For example you have a type called StringList which can hold string in comma seperated format and convert it back and forth to Enumerable. You want to supply this as ItemsSource of a list. For this you need the following TypeConverter


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApplication3
{
    public class MyTypeconverter:TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(string);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            return value==null?string.Empty:string.Join(",", string.Join(",", ((StringList)value)));
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            return new StringList((string)value);
        }

    }
}


Also you will need to decorate your type with TypeConverter attribute:-
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApplication3
{
    [TypeConverter(typeof(MyTypeconverter))]
    public class StringList : IEnumerable<string>
    {
        private readonly IEnumerable<string> _enumerable;
        private readonly string _original;

        public StringList(string value)
        {
            _original = value;
            if (!string.IsNullOrEmpty(value))
            {
                _enumerable = value.Split(",;".ToCharArray()).
                  Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => i.Trim());
            }
        }

        protected StringList(IEnumerable<string> value)
        {
            _enumerable = value;
            _original = string.Join(", ", value);
        }

        public StringList() { }

        public IEnumerator<string> GetEnumerator()
        {
            return _enumerable.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _enumerable.GetEnumerator();
        }

        public override string ToString()
        {
            return _original;
        }
    }
}


That's all, now you can use your type in xaml.


No comments:

Post a Comment