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;
}
}
}
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.