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.


Friday, 5 July 2013

Special MVVM scenarios

You want to use a custom Binding but the control does not support binding in the area you want. What are your options? Which would you choose and why?


For example, you have a textbox and you want to associate a command to it which is fired for every key up event. Also you want another command on the same textbox to be fired on key return. What can you do?

Answer:-

The easiest solution is to create an attached behavior. Let us create a class with a dependency property


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace SU.Behaviours
{
    public static class KeyUpBehaviour
    {
        public static DependencyProperty KeyUpCommandProperty = DependencyProperty.RegisterAttached("KeyUp",
                    typeof(ICommand),
                    typeof(KeyUpBehaviour),
                    new FrameworkPropertyMetadata(null, new PropertyChangedCallback(KeyUpBehaviour.KeyUpChanged)));

        public static void SetKeyUp(DependencyObject target, ICommand value)
        {
            target.SetValue(KeyUpBehaviour.KeyUpCommandProperty, value);
        }

        public static ICommand GetKeyUp(DependencyObject target)
        {
            return (ICommand)target.GetValue(KeyUpCommandProperty);
        }


        private static void KeyUpChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = target as UIElement;
            if (element != null)
            {
                // If we're putting in a new command and there wasn't one already
                // hook the event
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    element.KeyUp += new KeyEventHandler(element_KeyUp);
                }
                // If we're clearing the command and it wasn't already null
                // unhook the event
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.KeyUp -= element_KeyUp;
                }
            }
        }

        static void element_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down || e.Key == Key.Up)
                return;

            UIElement element = (UIElement)sender;
            ICommand command = (ICommand)element.GetValue(KeyUpBehaviour.KeyUpCommandProperty);
            command.Execute(null);
        }
    }

    //Creating our own Delegate Command
    public sealed class DelegateCommand : ICommand
    {
        public delegate void SimpleEventHandler();
        private SimpleEventHandler handler;
        private bool isEnabled = true;

        public event EventHandler CanExecuteChanged;

        public DelegateCommand(SimpleEventHandler handler)
        {
            this.handler = handler;
        }

        private void OnCanExecuteChanged()
        {
            if (this.CanExecuteChanged != null)
            {
                this.CanExecuteChanged(this, EventArgs.Empty);
            }
        }

        bool ICommand.CanExecute(object arg)
        {
            return this.IsEnabled;
        }

        void ICommand.Execute(object arg)
        {
            this.handler();
        }

        public bool IsEnabled
        {
            get
            {
                return this.isEnabled;
            }
            set
            {
                this.isEnabled = value;
                this.OnCanExecuteChanged();
            }
        }
    }

}


Similarly we can also have another one for key return:-


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace SU.Behaviours
{
    public static class KeyReturnBehaviour
    {
        public static DependencyProperty KeyReturnCommandProperty = DependencyProperty.RegisterAttached("KeyReturn",
                    typeof(ICommand),
                    typeof(KeyReturnBehaviour),
                    new FrameworkPropertyMetadata(null, new PropertyChangedCallback(KeyReturnBehaviour.KeyReturnChanged)));

        public static void SetKeyReturn(DependencyObject target, ICommand value)
        {
            target.SetValue(KeyReturnBehaviour.KeyReturnCommandProperty, value);
        }

        public static ICommand GetKeyReturn(DependencyObject target)
        {
            return (ICommand)target.GetValue(KeyReturnCommandProperty);
        }


        private static void KeyReturnChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = target as UIElement;
            if (element != null)
            {
                // If we're putting in a new command and there wasn't one already
                // hook the event
                if ((e.NewValue != null) && (e.OldValue == null))
                {
                    element.KeyUp += new KeyEventHandler(element_KeyReturn);
                }
                // If we're clearing the command and it wasn't already null
                // unhook the event
                else if ((e.NewValue == null) && (e.OldValue != null))
                {
                    element.KeyUp -= element_KeyReturn;
                }
            }
        }

        static void element_KeyReturn(object sender, KeyEventArgs e)
        {
            if (e.Key != Key.Return)
                return;

            UIElement element = (UIElement)sender;
            ICommand command = (ICommand)element.GetValue(KeyReturnBehaviour.KeyReturnCommandProperty);
            command.Execute(null);
        }
    }

}



Formatting Strings in XAML with multibinding


          <TextBlock.Text>
            <MultiBinding StringFormat="{}{0} x {1} Test">
              <Binding ElementName="sl1" Path="Value" />
              <Binding ElementName="sl2" Path="Value" />
            </MultiBinding>