Click here to Skip to main content
11,632,011 members (73,196 online)
Click here to Skip to main content

ObjectPresenter - How to Generate an Object's Testing GUI from a Given Object

, 5 Jan 2012 CPOL 17.7K 739 46
Rate this:
Please Sign up or sign in to vote.
In this article, I explain step by step, how we can create a WPF custom control that gets an object and, generates a GUI that enables editing that object's properties and invoking that object's methods.

Table of Contents

Introduction

When we want to test our .NET applications, in addition to the entire application test, we sometimes want to give a way to test some of our algorithms individually.

Usually, for achieving this goal, I used to create some GUI screens that enable entering the needed parameters and, invoking the wanted methods using them.

In that way, frequently, I found myself wasting a lot of time only for developing GUI screens that aren't a part of the developed application. Beside the first development of the screens, every time that a method's signature had been changed, I had to change those GUI screens too. In that way, every module that I had to test, gave me another screen to maintain.

Since I got tired of doing that any time I needed to test a class, I decided to write a control that simplifies this task.

Background

In this solution, we create a WPF control that can present the needed GUI for every class that we want to test and, is updated according to the changes of that class.

For presenting values, we create view-models and, appropriate DataTemplate for presenting them.

For more information about it, you can read the MSDN topics about Reflection, Control Authoring, Routed Events, Commands and, the MVVM pattern.

How It Works

Handling Values

Value view-model

Before creating a control that enables methods' invoke, we need a way to get the values of the methods' parameters. for that purpose, we create a view-model that presents the value's details:

public class BaseViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }

    #endregion
}

class ValueViewModel : BaseViewModel
{
}

Add properties for the value's name, values' type and, the value's value:

#region Name
private string _name;
public string Name
{
    get { return _name; }
    protected set
    {
        if (_name != value)
        {
            _name = value;
            NotifyPropertyChanged("Name");
            NotifyPropertyChanged("HasName");
        }
    }
}
#endregion

#region HasName
public bool HasName
{
    get { return !string.IsNullOrEmpty(Name); }
}
#endregion

#region ValueType
private Type _valueType;
public Type ValueType
{
    get { return _valueType; }
    set
    {
        if (_valueType != value)
        {
            Type oldValue = _valueType;
            _valueType = value;
            OnValueTypeChanged(oldValue, _valueType);
        }
    }
}

protected virtual void OnValueTypeChanged(Type oldValue, Type newValue)
{
    NotifyPropertyChanged("ValueType");
}
#endregion

#region Value
protected object _value;
public virtual object Value
{
    get { return _value; }
    set
    {
        if (_value != value)
        {
            object oldValue = _value;
            _value = value;
            OnValueChanged(oldValue, _value);
        }
    }
}

protected virtual void OnValueChanged(object oldValue, object newValue)
{
    NotifyPropertyChanged("Value");
}
#endregion

And, add properties for indicating the value's kind and content:

#region IsCollection
public bool IsCollection
{
    get
    {
        Type t = SelectedCompatibleType;
        if (t == null)
        {
            return false;
        }

        if (t == typeof(string))
        {
            return false;
        }

        Type[] typeIntefaces = t.GetInterfaces();
        if (typeIntefaces.Contains(typeof(IEnumerable)))
        {
            return true;
        }

        return false;
    }
}
#endregion

#region IsString
public bool IsString
{
    get { return SelectedCompatibleType == typeof(string); }
}
#endregion

#region IsParsable
public bool IsParsable
{
    get
    {
        Type t = SelectedCompatibleType;
        if (t == null)
        {
            return false;
        }

        MethodInfo parseMethod = t.GetMethod("Parse", new[] { typeof(string) });
        if (parseMethod != null && parseMethod.IsStatic)
        {
            return true;
        }

        return false;
    }
}
#endregion

#region IsEnum
public bool IsEnum
{
    get
    {
        Type t = SelectedCompatibleType;
        if (t == null)
        {
            return false;
        }

        return t.IsEnum;
    }
}
#endregion

#region IsBoolean
public bool IsBoolean
{
    get { return SelectedCompatibleType == typeof(bool); }
}
#endregion

Handling Derived Types

In order to support getting values of types that derive from the parameters' values' types, we have to find the whole of the types that compatible to the parameter's type. That can be done as the following:

#region KnownTypes
private IEnumerable<Type> _knownTypes;
public IEnumerable<Type> KnownTypes
{
    get { return _knownTypes; }
    set
    {
        if (_knownTypes != value)
        {
            IEnumerable<Type> oldValue = _knownTypes;
            _knownTypes = value;
            OnKnownTypesChanged(oldValue, _knownTypes);
        }
    }
}

protected virtual void OnKnownTypesChanged
    (IEnumerable<Type> oldValue, IEnumerable<Type> newValue)
{
    NotifyPropertyChanged("KnownTypes");
}
#endregion

#region AutoGenerateCompatibleTypes
private bool _autoGenerateCompatibleTypes;
public bool AutoGenerateCompatibleTypes
{
    get { return _autoGenerateCompatibleTypes; }
    set
    {
        if (_autoGenerateCompatibleTypes != value)
        {
            bool oldValue = _autoGenerateCompatibleTypes;
            _autoGenerateCompatibleTypes = value;
            OnAutoGenerateCompatibleTypesChanged(oldValue, _autoGenerateCompatibleTypes);
        }
    }
}

protected virtual void OnAutoGenerateCompatibleTypesChanged(bool oldValue, bool newValue)
{
    NotifyPropertyChanged("AutoGenerateCompatibleTypes");
}
#endregion

private List<Type> GetCompatibleTypes(Type baseType)
{
    List<Type> res = new List<Type>();

    if (!baseType.IsAbstract)
    {
        res.Add(baseType);
    }

    AddKnownCompatibleTypes(baseType, res);

    if (AutoGenerateCompatibleTypes &&
        baseType != typeof(object))
    {
        AddCompatibleTypesFromLoadedAssemblies(baseType, res);
    }

    return res.Distinct().ToList();
}

private void AddKnownCompatibleTypes(Type baseType, List<Type> res)
{
    if (KnownTypes != null)
    {
        foreach (Type t in KnownTypes)
        {
            if (t.IsSubclassOf(baseType) && !t.IsAbstract)
            {
                res.Add(t);
            }
        }
    }
}

private static void AddCompatibleTypesFromLoadedAssemblies(Type baseType, List<Type> res)
{
    Assembly[] assemblies = GetLoadedAssemblies();
    if (assemblies == null)
    {
        return;
    }

    foreach (Assembly a in assemblies)
    {
        if (a == null)
        {
            continue;
        }

        foreach (Type t in a.GetTypes())
        {
            if (t.IsSubclassOf(baseType) && !t.IsAbstract)
            {
                res.Add(t);
            }
        }
    }
}

static private Assembly[] GetLoadedAssemblies()
{
    AppDomain currDomain = AppDomain.CurrentDomain;
    if (currDomain == null)
    {
        return null;
    }

    return currDomain.GetAssemblies();
}

The GetCompatibleTypes method generates a list with the parameter's type (if it isn't abstract) and, types in the loaded assemblies that derive from the parameter's type. There is also an option to generate types only from a given types list, using the KnownTypes and the AutoGenerateCompatibleTypes properties appropriately.

We generate the compatible types and set the default selected compatible type, for each time the ValueType is changed, as the following:

#region CompatibleTypes
private ObservableCollection<Type> _compatibleTypes;
public ObservableCollection<Type> CompatibleTypes
{
    get { return _compatibleTypes ?? (_compatibleTypes = new ObservableCollection<Type>()); }
}
#endregion

#region SelectedCompatibleType
private Type _selectedCompatibleType;
public Type SelectedCompatibleType
{
    get { return _selectedCompatibleType; }
    set
    {
        if (_selectedCompatibleType != value)
        {
            Type oldValue = _selectedCompatibleType;
            _selectedCompatibleType = value;
            OnSelectedCompatibleTypeChanged(oldValue, _selectedCompatibleType);
        }
    }
}

protected virtual void OnSelectedCompatibleTypeChanged(Type oldValue, Type newValue)
{
    NotifyPropertyChanged("IsString");
    NotifyPropertyChanged("IsParsable");
    NotifyPropertyChanged("IsCollection");
    NotifyPropertyChanged("IsEnum");
    NotifyPropertyChanged("IsBoolean");

    NotifyPropertyChanged("Value");
}
#endregion

protected virtual void OnValueTypeChanged(Type oldValue, Type newValue)
{
    UpdateCompatibleTypes();

    NotifyPropertyChanged("ValueType");
}

protected void UpdateCompatibleTypes()
{
    CompatibleTypes.Clear();

    if (ValueType == null)
    {
        return;
    }

    GetCompatibleTypes(ValueType).ForEach(t => CompatibleTypes.Add(t));
    SelectedCompatibleType = CompatibleTypes.FirstOrDefault();
}

Handling Complex Types

In order to support getting values of complex types, we have to present the value of the properties and the fields of the type. That can be done as the following:

#region SubFields
private ObservableCollection<ValueViewModel> _subFields;
public ObservableCollection<ValueViewModel> SubFields
{
    get { return _subFields ?? (_subFields = new ObservableCollection<ValueViewModel>()); }
}
#endregion

#region HasSubFields
public bool HasSubFields
{
    get { return SubFields.Count != 0; }
}
#endregion

protected virtual void GenerateSubFieldsIfNeeded()
{
    SubFields.Clear();

    Type t = SelectedCompatibleType;

    if (t == null)
    {
        return;
    }

    if (IsString ||
        IsParsable ||
        IsCollection ||
        IsEnum)
    {
        return;
    }

    PropertyInfo[] typeProperties = t.GetProperties();
    foreach (PropertyInfo pi in typeProperties)
    {
        if (pi.CanWrite)
        {
            AddPropertyValueViewModel(pi);
        }
    }

    FieldInfo[] typeFields = t.GetFields();
    foreach (FieldInfo fi in typeFields)
    {
        if (fi.IsPublic)
        {
            AddFieldValueViewModel(fi);
        }
    }
}

protected abstract void AddPropertyValueViewModel(PropertyInfo pi);

protected abstract void AddFieldValueViewModel(FieldInfo fi);

The GenerateSubFieldsIfNeeded method calls the AddPropertyValueViewModel abstract method for the properties of the type and, calls the AddFieldValueViewModel abstract method for the fields of the type. These methods are implemented in the derived classes appropriately.

For getting values, we derive ValueViewModel as the following:

public class InputValueViewModel : ValueViewModel
{
    public InputValueViewModel()
    {
        IsEditable = true;
    }

    protected override void AddPropertyValueViewModel(PropertyInfo pi)
    {
        PropertyInputValueViewModel subField = new PropertyInputValueViewModel(pi)
        {
            KnownTypes = KnownTypes,
            AutoGenerateCompatibleTypes = AutoGenerateCompatibleTypes
        };

        SubFields.Add(subField);
    }

    protected override void AddFieldValueViewModel(FieldInfo fi)
    {
        FieldInputValueViewModel subField = new FieldInputValueViewModel(fi)
        {
            KnownTypes = KnownTypes,
            AutoGenerateCompatibleTypes = AutoGenerateCompatibleTypes
        };

        SubFields.Add(subField);
    }
}

public class PropertyInputValueViewModel : InputValueViewModel
{
    public PropertyInputValueViewModel(PropertyInfo pi)
    {
        PropertyInformation = pi;
    }

    #region PropertyInformation
    private PropertyInfo _propertyInformation;
    public PropertyInfo PropertyInformation
    {
        protected get { return _propertyInformation; }
        set
        {
            if (_propertyInformation != value)
            {
                PropertyInfo oldValue = _propertyInformation;
                _propertyInformation = value;
                OnPropertyInformationChanged(oldValue, _propertyInformation);
            }

        }
    }

    protected virtual void OnPropertyInformationChanged
        (PropertyInfo oldValue, PropertyInfo newValue)
    {
        if (newValue == null)
        {
            return;
        }

        Name = newValue.Name;
        ValueType = newValue.PropertyType;
    }
    #endregion
}

public class FieldInputValueViewModel : InputValueViewModel
{
    public FieldInputValueViewModel(FieldInfo fi)
    {
        FieldInformation = fi;
    }

    #region FieldInformation
    private FieldInfo _fieldInformation;
    public FieldInfo FieldInformation
    {
        protected get { return _fieldInformation; }
        set
        {
            if (_fieldInformation != value)
            {
                FieldInfo oldValue = _fieldInformation;
                _fieldInformation = value;
                OnFieldInformationChanged(oldValue, _fieldInformation);
            }
        }
    }

    protected virtual void OnFieldInformationChanged
        (FieldInfo oldValue, FieldInfo newValue)
    {
        if (newValue == null)
        {
            return;
        }

        Name = newValue.Name;
        ValueType = newValue.FieldType;
    }
    #endregion
}

The IsEditable property is defined in ValueViewModel as the following:

#region IsEditable
private bool _isEditable;
public bool IsEditable
{
    get { return _isEditable; }
    protected set
    {
        if (_isEditable != value)
        {
            _isEditable = value;
            NotifyPropertyChanged("IsEditable");
        }
    }
}
#endregion

For presenting values, we derive ValueViewModel as the following:

public class OutputValueViewModel : ValueViewModel
{
    #region Constructors
    public OutputValueViewModel(object value)
    {
        IsEditable = false;

        if (value != null)
        {
            Type valueType = value.GetType();
            if (valueType.IsEnum)
            {
                // Set the enum as string.
                Value = Enum.GetName(valueType, value);
                ValueType = typeof(string);
            }
            else if (value is Exception)
            {
                Value = new ExceptionData(value as Exception);
                ValueType = typeof(ExceptionData);
            }
            else
            {
                Value = value;
                ValueType = value.GetType();
            }
        }
    }
    #endregion

    protected override void AddFieldValueViewModel(FieldInfo fi)
    {
        if (fi == null || Value == null)
        {
            return;
        }

        object fieldValue = fi.GetValue(Value);
        SubFields.Add(new OutputValueViewModel(fieldValue)
        {
            Name = fi.Name
        });
    }

    protected override void AddPropertyValueViewModel(PropertyInfo pi)
    {
        if (pi == null || Value == null)
        {
            return;
        }

        object prorertyValue = pi.GetValue(Value, null);
        SubFields.Add(new OutputValueViewModel(prorertyValue)
        {
            Name = pi.Name
        });
    }
}

The OutputValueViewModel class uses the ExceptionData class for presenting exceptions. This class is implemented as the following:

public class ExceptionData
{
    public ExceptionData(Exception ex)
    {
        if (ex != null)
        {
            ExceptionType = ex.GetType().FullName;
            ExceptionMessage = ex.Message;

            if (ex.InnerException != null)
            {
                InnerException = new ExceptionData(ex.InnerException);
            }
        }
    }

    public string ExceptionType { get; set; }
    public string ExceptionMessage { get; set; }
    public ExceptionData InnerException { get; set; }
}

Handling Collections

In order to support getting collections, we have to let the user to add and remove collections' elements. We can do that by adding commands for adding and removing collections' elements:

#region CollectionElements
private ObservableCollection<ValueViewModel> _collectionElements;
public ObservableCollection<ValueViewModel> CollectionElements
{
    get { return _collectionElements ?? 
    (_collectionElements = new ObservableCollection<ValueViewModel>()); }
}
#endregion

#region IsRemovable
private bool _isRemovable;
public bool IsRemovable
{
    get { return _isRemovable; }
    protected set
    {
        if (_isRemovable != value)
        {
            _isRemovable = value;
            NotifyPropertyChanged("IsRemovable");
        }
    }
}
#endregion

#region AddNewCollectionElementCommand
private ICommand _addNewCollectionElementCommand;
public ICommand AddNewCollectionElementCommand
{
    get
    {
        if (_addNewCollectionElementCommand == null)
        {
            _addNewCollectionElementCommand =
                new GeneralCommand(o => AddNewCollectionElement(true), o => IsCollection);
        }

        return _addNewCollectionElementCommand;
    }
}

protected void AddNewCollectionElement(bool notifyValueChanged)
{
    InputValueViewModel element = new InputValueViewModel
    {
        ValueType = CollectionElementType,
        IsRemovable = true,
        Removed = OnCollectionElementRemoved,
        KnownTypes = KnownTypes,
        AutoGenerateCompatibleTypes = AutoGenerateCompatibleTypes
    };

    CollectionElements.Add(element);

    if (notifyValueChanged)
    {
        NotifyPropertyChanged("Value");
    }
}

protected void OnCollectionElementRemoved(ValueViewModel element)
{
    CollectionElements.Remove(element);
}
#endregion

#region RemoveCommand
private ICommand _removeCommand;
public ICommand RemoveCommand
{
    get { return _removeCommand ?? 
    (_removeCommand = new GeneralCommand(o => Remove(), o => IsRemovable)); }
}

protected void Remove()
{
    Action<ValueViewModel> handler = Removed;
    if (handler != null)
    {
        handler(this);
    }
}

public Action<ValueViewModel> Removed;
#endregion

For creating the commands, we use the GeneralCommand class. This class is implemented as the following:

public class GeneralCommand : ICommand
{
    private Action<object> _execute;
    private Predicate<object> _canExecute;

    public GeneralCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        if (_canExecute != null)
        {
            return _canExecute(parameter);
        }

        return true;
    }

    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }

    public void Execute(object parameter)
    {
        if (_execute != null)
        {
            _execute(parameter);
        }
    }

    #endregion
}

For presenting collections, we override the GenerateSubFieldsIfNeeded method in OutputValueViewModel as the following:

protected override void GenerateSubFieldsIfNeeded()
{
    base.GenerateSubFieldsIfNeeded();

    if (IsCollection)
    {
        IEnumerable ie = Value as IEnumerable;
        if (ie != null)
        {
            foreach (object val in ie)
            {
                CollectionElements.Add(new OutputValueViewModel(val));
            }
        }
    }
}

Handling Null Values

In order to support getting null values, we add properties to indicate that the value is null:

#region IsNullable
public bool IsNullable
{
    get
    {
        Type t = SelectedCompatibleType;

        return t != null ? !t.IsValueType : false;
    }
}
#endregion

#region IsNull
protected bool _isNull;
public bool IsNull
{
    get { return _isNull; }
    set
    {
        if (_isNull != value)
        {
            bool oldValue = _isNull;
            _isNull = value;
            OnIsNullChanged(oldValue, _isNull);
            NotifyPropertyChanged("IsNull");
        }
    }
}

protected virtual void OnIsNullChanged(bool oldValue, bool newValue)
{
}
#endregion

Value data-template

After we have the view-model, we create a DataTemplate for presenting this view-model:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
</DataTemplate>

In this DataTemplate, we put a Grid and, separate it to 3 columns:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
    <Grid x:Name="rootElement"
            HorizontalAlignment="Stretch"
            Margin="1">
        <Grid.LayoutTransform>
            <ScaleTransform ScaleX="0" ScaleY="0" />
        </Grid.LayoutTransform>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
    </Grid>
</DataTemplate>

In this Grid, we add a Remove button:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
    <DataTemplate.Resources>
        <Storyboard x:Key="hideRootElementStoryboard">
            <DoubleAnimation Storyboard.TargetName="rootElement"
                                 Storyboard.TargetProperty="(FrameworkElement.
                LayoutTransform).(ScaleTransform.ScaleX)"
                                 To="0"
                                 Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="rootElement"
                                 Storyboard.TargetProperty="(FrameworkElement.
                LayoutTransform).(ScaleTransform.ScaleY)"
                                 To="0"
                                 Duration="0:0:0.2" />
        </Storyboard>
    </DataTemplate.Resources>
    <Grid x:Name="rootElement"
              HorizontalAlignment="Stretch"
              Margin="1">
        ...
        <local:SuspendedButton x:Name="btnRemove"
                                   Style="{StaticResource removeButtonStyle}"
                                   ToolTip="Remove element."
                                   SuspendTime="0:0:0.2"
                                   Margin="1"
                                   Command="{Binding RemoveCommand}"
                                   Visibility="Collapsed"
                                   VerticalAlignment="Top"
                                   HorizontalAlignment="Left" />
    </Grid>
    <DataTemplate.Triggers>
            <EventTrigger RoutedEvent="local:SuspendedButton.BeforeClick"
                          SourceName="btnRemove">
                <BeginStoryboard Storyboard=
            "{StaticResource hideRootElementStoryboard}" />
            </EventTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Add a region for the field's name and value:

<Grid x:Name="rootElement"
            HorizontalAlignment="Stretch"
            Margin="1">
    ...
    <Grid x:Name="fieldRegion"
            Grid.Column="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel Orientation="Horizontal"
                    Visibility="{Binding HasName, 
            Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock Text="{Binding Name}" />
            <TextBlock Text=": " />
        </StackPanel>

        <Grid x:Name="isNullRegion"
                Grid.Column="1"
                Margin="2">
            <ToggleButton Visibility="{Binding IsNullable, 
        Converter={StaticResource BooleanToVisibilityConverter}}"
                            Style="{StaticResource nullButtonStyle}"
                            VerticalAlignment="Top"
                            IsChecked="{Binding IsNull, Mode=TwoWay}" />
        </Grid>

        <ContentControl x:Name="fieldValue"
                        Content="{Binding}"
                        Grid.Column="2" />
    </Grid>
</Grid>

and, add a region for additional compatible types:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
    <DataTemplate.Resources>
        ...
        <Storyboard x:Key="showTypesStoryboard">
            <DoubleAnimation Storyboard.TargetName="typesBorder"
                                Storyboard.TargetProperty=
            "(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)"
                                To="1"
                                Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="typesBorder"
                                Storyboard.TargetProperty=
            "(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"
                                To="1"
                                Duration="0:0:0.2" />
        </Storyboard>
        <Storyboard x:Key="hideTypesStoryboard">
            <DoubleAnimation Storyboard.TargetName="typesBorder"
                                Storyboard.TargetProperty=
            "(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleX)"
                                To="0"
                                Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="typesBorder"
                                Storyboard.TargetProperty=
            "(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"
                                To="0"
                                Duration="0:0:0.2" />
        </Storyboard>
    </DataTemplate.Resources>
    <Grid x:Name="rootElement"
            HorizontalAlignment="Stretch"
            Margin="1">
        ...
        <Grid Visibility="{Binding HasAdditionalCompatibleTypes, 
        Converter={StaticResource BooleanToVisibilityConverter}}"
              Grid.Column="2"
              VerticalAlignment="Top"
              Margin="5,0,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <ToggleButton x:Name="typesToggle"
                          Style="{StaticResource expandButtonStyle}"
                          HorizontalAlignment="Right"
                          ToolTip="Expand the compatible types region.">
                <ToggleButton.LayoutTransform>
                    <ScaleTransform ScaleX="-1" />
                </ToggleButton.LayoutTransform>
            </ToggleButton>
            <Border x:Name="typesBorder"
                    Grid.Row="1"
                    Margin="0,3"
                    BorderThickness="1"
                    CornerRadius="3"
                    BorderBrush="#CC000000"
                    Background="#44000000">
                <Border.LayoutTransform>
                    <ScaleTransform ScaleX="0" ScaleY="0" />
                </Border.LayoutTransform>
                <StackPanel Margin="5">
                    <TextBlock Text="Types"
                                HorizontalAlignment="Center"
                                FontSize="14"
                                Margin="0,0,0,5"
                                Foreground="#EEFFFFFF"/>
                    <ListBox ItemsSource="{Binding CompatibleTypes}"
                             SelectedItem="{Binding SelectedCompatibleType, Mode=TwoWay}"
                             Background="Transparent"
                             BorderBrush="Transparent"
                             MaxHeight="250">
                        <ListBox.ItemContainerStyle>
                            <Style TargetType="{x:Type ListBoxItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                            <Grid>
                                                <Border x:Name="selectedTypeBackground"
                                                        Background="#99FFFFFF"
                                                        BorderThickness="1"
                                                        BorderBrush="#DDFFFFFF"
                                                        CornerRadius="3"
                                                        Visibility="Hidden"/>
                                                <ContentPresenter Margin="3" />
                                            </Grid>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsSelected"
                                                         Value="True">
                                                    <Setter TargetName=
                        "selectedTypeBackground"
                                                            Property="Visibility"
                                                            Value="Visible" />
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ListBox.ItemContainerStyle>
                    </ListBox>
                </StackPanel>
            </Border>
        </Grid>
    </Grid>
    <DataTemplate.Triggers>
        ...
        <Trigger SourceName="typesToggle"
                 Property="IsChecked"
                 Value="True">
            <Setter TargetName="typesToggle"
                    Property="ToolTip"
                    Value="Collapse the compatible types region." />
            <Trigger.EnterActions>
                <BeginStoryboard Storyboard="{StaticResource showTypesStoryboard}"/>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard Storyboard="{StaticResource hideTypesStoryboard}" />
            </Trigger.ExitActions>
        </Trigger>
    </DataTemplate.Triggers>
</DataTemplate>

For the Remove button, we use a SuspendedButton, in order to run an animation before the field is removed.

In the compatible types region, we have a ListBox for selecting the value's type.

In the field's region, we have a TextBlock for presenting the field's name, a ToggleButton that determines if the value is null and, a ContentControl for presenting the field's value.

In order to present different types in different ways, we create data-templates for the types:

<DataTemplate x:Key="regularFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

<DataTemplate x:Key="booleanFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

<DataTemplate x:Key="enumFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

<DataTemplate x:Key="complexFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

<DataTemplate x:Key="collectionFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

<DataTemplate x:Key="nullFieldDataTemplate"
              DataType="{x:Type local:ValueViewModel}">
    ...
</DataTemplate>

and, change the ContentTemplate of the value's ContentControl appropriately:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
    ...
    <DataTemplate.Triggers>
        ...
        <DataTrigger Binding="{Binding IsString}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource regularFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding IsParsable}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource regularFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding HasSubFields}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource complexFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding IsCollection}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource collectionFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding IsEnum}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource enumFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding IsBoolean}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource booleanFieldDataTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding IsNull}"
                            Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{StaticResource nullFieldDataTemplate}" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Additional data-templates

In order to support different data-templates for specific types, we have to give the user a way to define specific data-templates for the wanted types. We can do that by holding the types' data-templates in ValueViewModel:

public class TypeDataTemplate : BaseViewModel
{
    #region Properties

    #region ValueType
    private Type _valueType;
    public Type ValueType
    {
        get { return _valueType; }
        set
        {
            if (_valueType != value)
            {
                _valueType = value;
                NotifyPropertyChanged("ValueType");
            }
        }
    }
    #endregion

    #region ValueViewModelDataTemplate
    private DataTemplate _valueViewModelDataTemplate;
    public DataTemplate ValueViewModelDataTemplate
    {
        get { return _valueViewModelDataTemplate; }
        set
        {
            if (_valueViewModelDataTemplate != value)
            {
                _valueViewModelDataTemplate = value;
                NotifyPropertyChanged("ValueViewModelDataTemplate");
            }
        }
    }
    #endregion

    #endregion
}

public abstract class ValueViewModel : BaseViewModel
{
    ...
    #region DataTemplates
    private IEnumerable<TypeDataTemplate> _dataTemplates;
    public IEnumerable<TypeDataTemplate> DataTemplates
    {
        get { return _dataTemplates; }
        set
        {
            if (_dataTemplates != value)
            {
                IEnumerable<TypeDataTemplate> oldValue = _dataTemplates;
                _dataTemplates = value;
                OnDataTemplatesChanged(oldValue, _dataTemplates);
                NotifyPropertyChanged("DataTemplates");
            }
        }
    }

    protected virtual void OnDataTemplatesChanged
    (IEnumerable<TypeDataTemplate> oldValue, IEnumerable<TypeDataTemplate> newValue)
    {
    }
    #endregion
    ...
}

Setting the appropriate data-template (if there is) according to the SelectedCompatibleType:

protected virtual void OnDataTemplatesChanged(IEnumerable<TypeDataTemplate> oldValue,
    IEnumerable<TypeDataTemplate> newValue)
{
    ValueDataTemplate = null;

    if (newValue != null)
    {
        Type t = SelectedCompatibleType;
        if (t != null)
        {
            ValueDataTemplate = newValue.Where(tdt1 => tdt1.ValueType == t).
                Select(tdt2 => tdt2.ValueViewModelDataTemplate).FirstOrDefault();
        }

        foreach (ValueViewModel subField in SubFields)
        {
            subField.DataTemplates = newValue;
        }

        foreach (ValueViewModel elem in CollectionElements)
        {
            elem.DataTemplates = newValue;
        }
    }
}

#region ValueDataTemplate
private DataTemplate _valueDataTemplate;
public DataTemplate ValueDataTemplate
{
    get { return _valueDataTemplate; }
    set
    {
        if (_valueDataTemplate != value)
        {
            _valueDataTemplate = value;
            NotifyPropertyChanged("ValueDataTemplate");
            NotifyPropertyChanged("HasValueDataTemplate");
        }
    }
}
#endregion

#region HasValueDataTemplate
public bool HasValueDataTemplate
{
    get { return ValueDataTemplate != null; }
}
#endregion

and, adding a DataTrigger for setting the appropriate DataTemplate:

<DataTemplate DataType="{x:Type local:ValueViewModel}">
    ...
    <DataTemplate.Triggers>
    ...
        <DataTrigger Binding="{Binding HasValueDataTemplate}"
                        Value="True">
            <Setter TargetName="fieldValue"
                        Property="ContentTemplate"
                        Value="{Binding ValueDataTemplate}" />
        </DataTrigger>
     </DataTemplate.Triggers>
</DataTemplate>

Buttons styles

In addition to the data-templates, we add styles for some of the buttons.

For the Remove button, we add the following style (x):

<Style x:Key="removeButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="BorderThickness"
            Value="1" />
    <Setter Property="BorderBrush"
            Value="DarkRed" />
    <Setter Property="Background"
            Value="Red" />
    <Setter Property="Foreground"
            Value="White" />
    <Setter Property="Width"
            Value="15" />
    <Setter Property="Height"
            Value="15" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="mouseEnterStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="rootElement"
                                            To="1"
                                            Duration="0:0:0.05" />
                    </Storyboard>
                    <Storyboard x:Key="mouseLeaveStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="rootElement"
                                            To="0.5"
                                            Duration="0:0:0.15" />
                    </Storyboard>
                </ControlTemplate.Resources>
                <Grid x:Name="rootElement"
                        Opacity="0.5">
                    <Ellipse Stroke="{TemplateBinding BorderBrush}"
                                StrokeThickness="1"
                                Fill="{TemplateBinding Background}" />
                    <Line X1="1" Y1="1" X2="7" Y2="7"
                            Stroke="{TemplateBinding Foreground}"
                            StrokeThickness="2"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center" />
                    <Line X1="1" Y1="7" X2="7" Y2="1"
                            Stroke="{TemplateBinding Foreground}"
                            StrokeThickness="2"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Center" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver"
                                Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource mouseEnterStoryboard}" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource mouseLeaveStoryboard}" />
                        </Trigger.ExitActions>
                    </Trigger>
                    <Trigger Property="IsPressed"
                                Value="True">
                        <Setter Property="Background"
                                Value="#FFCC0000" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

for the Expand button, we add the following style ():

<Style x:Key="expandButtonStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="BorderThickness"
            Value="1" />
    <Setter Property="BorderBrush"
            Value="DarkBlue" />
    <Setter Property="Background"
            Value="LightBlue" />
    <Setter Property="Foreground"
            Value="Cyan" />
    <Setter Property="Width"
            Value="12" />
    <Setter Property="Height"
            Value="12" />
    <Setter Property="Padding"
            Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="mouseEnterStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="rootElement"
                                            To="1"
                                            Duration="0:0:0.05" />
                    </Storyboard>
                    <Storyboard x:Key="mouseLeaveStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="rootElement"
                                            To="0.5"
                                            Duration="0:0:0.15" />
                    </Storyboard>
                    <Storyboard x:Key="checkedStoryboard">
                        <DoubleAnimation Storyboard.TargetName="mainShape"
                                Storyboard.TargetProperty=
                "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                To="45"
                                Duration="0:0:0.2" />
                    </Storyboard>
                    <Storyboard x:Key="uncheckedStoryboard">
                        <DoubleAnimation Storyboard.TargetName="mainShape"
                                Storyboard.TargetProperty=
                "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                To="-45"
                                Duration="0:0:0.2" />
                    </Storyboard>
                </ControlTemplate.Resources>
                <Grid x:Name="rootElement"
                        Opacity="0.5">
                    <Polygon x:Name="mainShape"
                                Stroke="{TemplateBinding BorderBrush}"
                                StrokeThickness="2"
                                StrokeLineJoin="Round"
                                Fill="{TemplateBinding Background}"
                                RenderTransformOrigin="0.5,0.5"
                                Points="0,10 10,10 10,0">
                        <Polygon.RenderTransform>
                            <RotateTransform Angle="-45" />
                        </Polygon.RenderTransform>
                    </Polygon>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver"
                                Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard=
            "{StaticResource mouseEnterStoryboard}" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource mouseLeaveStoryboard}" />
                        </Trigger.ExitActions>
                    </Trigger>
                    <Trigger Property="IsPressed"
                                Value="True">
                        <Setter Property="Background"
                                Value="#FF0000CC" />
                    </Trigger>
                    <Trigger Property="IsChecked"
                                Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource checkedStoryboard}" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource uncheckedStoryboard}" />
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and, for the Null button, we add the following style ():

<Style x:Key="nullButtonStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="BorderThickness"
            Value="1" />
    <Setter Property="BorderBrush"
            Value="#CC000000" />
    <Setter Property="Background"
            Value="#44000000" />
    <Setter Property="Foreground"
            Value="White" />
    <Setter Property="Width"
            Value="20" />
    <Setter Property="Height"
            Value="20" />
    <Setter Property="Padding"
            Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="mouseEnterStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="backgroundBorder"
                                            To="1"
                                            Duration="0:0:0.05" />
                    </Storyboard>
                    <Storyboard x:Key="mouseLeaveStoryboard">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                            Storyboard.TargetName="backgroundBorder"
                                            To="0.5"
                                            Duration="0:0:0.15" />
                    </Storyboard>
                </ControlTemplate.Resources>
                <Grid >
                    <Border x:Name="backgroundBorder"
                            Opacity="0.5"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            CornerRadius="2"/>
                    <Grid x:Name="nullSign"
                            Opacity="0.5">
                        <Ellipse Stroke="{TemplateBinding Foreground}"
                                StrokeThickness="2"
                                Fill="Transparent"
                                Margin="4" />
                        <Line X1="0" Y1="14" X2="14" Y2="0"
                            Stroke="{TemplateBinding Foreground}"
                                StrokeThickness="2"
                            Margin="3" />
                    </Grid>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver"
                                Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource mouseEnterStoryboard}" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard=
                "{StaticResource mouseLeaveStoryboard}" />
                        </Trigger.ExitActions>
                    </Trigger>
                    <Trigger Property="IsPressed"
                                Value="True">
                        <Setter Property="Background"
                                Value="#AA000000" />
                    </Trigger>
                    <Trigger Property="IsChecked"
                                Value="True">
                        <Setter TargetName="nullSign"
                                Property="Opacity"
                                Value="1" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Generate a value from the inputs

In order to get the parameter's value from the whole of the value's parts (e.g. collection's elements, class properties, etc.), we have to go over each part and generate its value appropriately. That can be done as the following:

protected virtual object GetValue()
{
    if (IsNullable && IsNull)
    {
        return null;
    }

    if (IsString && _value == null)
    {
        return string.Empty;
    }

    if (IsParsable)
    {
        return ParseIfNeeded(_value);
    }

    if (IsCollection)
    {
        return GenerateValueFromCollectionElements();
    }

    if (HasSubFields)
    {
        return GenerateValueFromSubFields();
    }

    if (IsEnum)
    {
        return GenerateEnumValue();
    }

    return _value;
}

In the ParseIfNeeded method, we get the value using the Parse method of the value's type.

In the GenerateValueFromCollectionElements method, we create a collection (according to the collection's type), get the values of the collection's elements and, set the values in the collection.

In the GenerateValueFromSubFields method, we create an object (according to the value's type) and, set its fields and properties according to the SubFields.

In the GenerateEnumValue method, we get the Enum value that is compatible to the input's string representation.

Handling Methods

Presenting Methods

After we have a view-model for getting values, we can get the parameters of a method and, invoke that method with the gotten parameters. For that purpose, we create a control that presents a method:

public class MethodPresenter : Control
{
    static MethodPresenter()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodPresenter),
            new FrameworkPropertyMetadata(typeof(MethodPresenter)));
    }
}

To this control, we add properties for the method's name:

#region MethodName
public string MethodName
{
    get { return (string)GetValue(MethodNameProperty); }
    protected set { SetValue(MethodNameProperty, value); }
}

public static readonly DependencyProperty MethodNameProperty =
    DependencyProperty.Register("MethodName", typeof(string),
        typeof(MethodPresenter), new UIPropertyMetadata(null));
#endregion

The object on which to invoke the method:

#region ObjectInstance
public object ObjectInstance
{
    get { return (object)GetValue(ObjectInstanceProperty); }
    set { SetValue(ObjectInstanceProperty, value); }
}

public static readonly DependencyProperty ObjectInstanceProperty =
    DependencyProperty.Register("ObjectInstance", typeof(object),
        typeof(MethodPresenter), new UIPropertyMetadata(null));
#endregion

and, properties to support derived types and additional data-templates:

#region KnownTypes
public IEnumerable<Type> KnownTypes
{
    get { return (IEnumerable<Type>)GetValue(KnownTypesProperty); }
    set { SetValue(KnownTypesProperty, value); }
}

public static readonly DependencyProperty KnownTypesProperty =
    DependencyProperty.Register("KnownTypes", typeof(IEnumerable<Type>),
        typeof(MethodPresenter), new UIPropertyMetadata(null));
#endregion

#region AutoGenerateCompatibleTypes
public bool AutoGenerateCompatibleTypes
{
    get { return (bool)GetValue(AutoGenerateCompatibleTypesProperty); }
    set { SetValue(AutoGenerateCompatibleTypesProperty, value); }
}

public static readonly DependencyProperty AutoGenerateCompatibleTypesProperty =
    DependencyProperty.Register("AutoGenerateCompatibleTypes", typeof(bool),
        typeof(MethodPresenter), new UIPropertyMetadata(true));
#endregion

#region DataTemplates
public IEnumerable<TypeDataTemplate> DataTemplates
{
    get { return (IEnumerable<TypeDataTemplate>)GetValue(DataTemplatesProperty); }
    set { SetValue(DataTemplatesProperty, value); }
}

public static readonly DependencyProperty DataTemplatesProperty =
    DependencyProperty.Register("DataTemplates", typeof(IEnumerable<TypeDataTemplate>),
        typeof(MethodPresenter), new UIPropertyMetadata(null));
#endregion

For getting the method's parameters, we add a property for holding the method's parameters:

#region MethodParameters
private ObservableCollection<InputValueViewModel> _methodParameters;
public ObservableCollection<InputValueViewModel> MethodParameters
{
    get
    {
        return _methodParameters ??
            (_methodParameters = new ObservableCollection<InputValueViewModel>());
    }
}
#endregion

Create a view-model for getting a method's parameter:

public class ParameterInputValueViewModel : InputValueViewModel
{
    public ParameterInputValueViewModel(ParameterInfo pi)
    {
        ParameterInformation = pi;
    }

    #region Properties

    #region ParameterInformation
    private ParameterInfo _parameterInformation;
    public ParameterInfo ParameterInformation
    {
        protected get { return _parameterInformation; }
        set
        {
            if (_parameterInformation != value)
            {
                ParameterInfo oldValue = _parameterInformation;
                _parameterInformation = value;
                OnParameterInformationChanged(oldValue, _parameterInformation);
            }

        }
    }

    protected virtual void OnParameterInformationChanged
        (ParameterInfo oldValue, ParameterInfo newValue)
    {
        if (newValue == null)
        {
            return;
        }

        Name = newValue.Name;
        ValueType = newValue.ParameterType;

    }
    #endregion

    #endregion
}

and, set the method's parameters according to a given method's information:

public MethodInfo MethodInformation
{
    get { return (MethodInfo)GetValue(MethodInformationProperty); }
    set { SetValue(MethodInformationProperty, value); }
}

public static readonly DependencyProperty MethodInformationProperty =
    DependencyProperty.Register("MethodInformation", typeof(MethodInfo),
        typeof(MethodPresenter), new UIPropertyMetadata(null, OnMethodInformationChanged));

private static void OnMethodInformationChanged
    (DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    MethodPresenter mp = sender as MethodPresenter;
    if (mp == null)
    {
        return;
    }

    mp.MethodParameters.Clear();

    if (mp.MethodInformation != null)
    {
        mp.MethodName = mp.MethodInformation.Name;

        mp.MethodInformation.GetParameters().ToList().
            ForEach(pi => mp.MethodParameters.Add(
                new ParameterInputValueViewModel(pi)
                {
                    KnownTypes = mp.KnownTypes,
                    AutoGenerateCompatibleTypes = mp.AutoGenerateCompatibleTypes,
                    DataTemplates = mp.DataTemplates
                }));
    }
    else
    {
        mp.MethodName = null;
    }
}

Invoking methods

After we have the method's parameters, we have to invoke the method. For that purpose, we add a RoutedCommand:

private static RoutedCommand _invokeMethodCommand;
public static RoutedCommand InvokeMethodCommand
{
    get
    {
        return _invokeMethodCommand ??
            (_invokeMethodCommand = new RoutedCommand
        ("InvokeMethod", typeof(MethodPresenter)));
    }
}

add CanExecute and Executed event-handlers:

private static void CanExecuteInvokeMethodCommand
        (object sender, CanExecuteRoutedEventArgs e)
{
    MethodPresenter mp = sender as MethodPresenter;
    if (mp == null)
    {
        return;
    }

    e.CanExecute = mp.MethodInformation != null && mp.ObjectInstance != null;
}

private static void ExecuteInvokeMethodCommand(object sender, ExecutedRoutedEventArgs e)
{
    MethodPresenter mp = sender as MethodPresenter;
    if (mp == null)
    {
        return;
    }

    mp.InvokeMethod();
}

private void InvokeMethod()
{
    MethodInfo mi = MethodInformation;
    object obj = ObjectInstance;

    if (mi == null || obj == null)
    {
        return;
    }

    object[] parameters = MethodParameters.Select(p => p.Value).ToArray();

    _invokeMethodThread = new Thread(() =>
    {
        OutputValueViewModel methodReturnValue = null;
        OutputValueViewModel methodException = null;

        try
        {
            object retVal = mi.Invoke(obj, parameters);
            methodReturnValue = mi.ReturnType != typeof(void) ? 
            new OutputValueViewModel(retVal) : null;
        }
        catch (System.Reflection.TargetInvocationException tie)
        {
            methodException = new OutputValueViewModel(tie.InnerException);
        }
        catch (Exception ex)
        {
            methodException = new OutputValueViewModel(ex);
        }
        finally
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                new ThreadStart(() =>
                {
                    if (methodReturnValue != null)
                    {
                        methodReturnValue.DataTemplates = DataTemplates;
                    }

                    if (methodException != null)
                    {
                        methodException.DataTemplates = DataTemplates;
                    }

                    MethodResultViewModel methodResult = new MethodResultViewModel
                    {
                        MethodName = mi.Name,
                        ResultTime = DateTime.Now,
                        MethodReturnValue = methodReturnValue,
                        MethodException = methodException
                    };

                    int paramInx = 0;
                    foreach (ParameterInfo pi in mi.GetParameters())
                    {
                        if (pi.IsOut || pi.ParameterType.IsByRef)
                        {
                            methodResult.MethodOutputs.Add(
                                new ParameterOutputValueViewModel
                (pi, parameters[paramInx])
                                {
                                    DataTemplates = DataTemplates
                                });
                        }

                        paramInx++;
                    }

                    methodResult.Removed += OnMethodResultRemoved;
                    methodResult.Shown += OnMethodResultShown;

                    if (StoreMethodResults)
                    {
                        MethodResults.Add(methodResult);
                        HasResults = true;
                    }

                    CurrentMethodResult = methodResult;
                }));
        }

        _invokeMethodThread = null;
    });

    _invokeMethodThread.Start();
}

private Thread _invokeMethodThread;

#region MethodResults
private ObservableCollection<MethodResultViewModel> _methodResults;
public ObservableCollection<MethodResultViewModel> MethodResults
{
    get
    {
        return _methodResults ?? (_methodResults = 
        new ObservableCollection<MethodResultViewModel>());
    }
}
#endregion

#region CurrentMethodResult
public MethodResultViewModel CurrentMethodResult
{
    get { return (MethodResultViewModel)GetValue(CurrentMethodResultProperty); }
    set { SetValue(CurrentMethodResultProperty, value); }
}

public static readonly DependencyProperty CurrentMethodResultProperty =
    DependencyProperty.Register("CurrentMethodResult", typeof(MethodResultViewModel),
        typeof(MethodPresenter), new UIPropertyMetadata(null));
#endregion

#region HasResults
public bool HasResults
{
    get { return (bool)GetValue(HasResultsProperty); }
    protected set { SetValue(HasResultsProperty, value); }
}

public static readonly DependencyProperty HasResultsProperty =
    DependencyProperty.Register("HasResults", typeof(bool),
        typeof(MethodPresenter), new UIPropertyMetadata(false));
#endregion

#region StoreMethodResults
public bool StoreMethodResults
{
    get { return (bool)GetValue(StoreMethodResultsProperty); }
    set { SetValue(StoreMethodResultsProperty, value); }
}

public static readonly DependencyProperty StoreMethodResultsProperty =
    DependencyProperty.Register("StoreMethodResults", typeof(bool),
        typeof(MethodPresenter), new UIPropertyMetadata(false));
#endregion

and, register the event-handlers with the command:

static MethodPresenter()
{
    ...
    CommandBinding invokeMethodBinding = new CommandBinding(InvokeMethodCommand,
        ExecuteInvokeMethodCommand, CanExecuteInvokeMethodCommand);

    CommandManager.RegisterClassCommandBinding
    (typeof(MethodPresenter), invokeMethodBinding);
}

In the InvokeMethod method, we get the method's parameters, invoke the method in a different thread (in order to prevent UI blocking) and, set the properties that influence on the UI elements, using the UI Dispatcher.

For presenting the result of the method, we use the MethodResultViewModel class. This class contains: properties for the result's details, a command for showing the result (this command fires the Shown event) and, a command for removing the result (this command fires the Removed event).

For notifying the user about the method's invoke, we add a RoutedEvent for raising before the method's invoke:

public class MethodInvokeRequestedRoutedEventArgs : RoutedEventArgs
{
    #region Constructors
    public MethodInvokeRequestedRoutedEventArgs()
    {
    }

    public MethodInvokeRequestedRoutedEventArgs(RoutedEvent routedEvent)
        : base(routedEvent)
    {
    }

    public MethodInvokeRequestedRoutedEventArgs(RoutedEvent routedEvent, object source)
        : base(routedEvent, source)
    {
    }
    #endregion

    public MethodInfo MethodInformation { get; set; }
    public object[] Parameters { get; set; }
}

public delegate void MethodInvokeRequestedRoutedEventHandler
    (object sender, MethodInvokeRequestedRoutedEventArgs e);

public class MethodPresenter : Control
{
    ...
    public static readonly RoutedEvent MethodInvokeRequestedEvent =
        EventManager.RegisterRoutedEvent("MethodInvokeRequested", RoutingStrategy.Bubble,
            typeof(MethodInvokeRequestedRoutedEventHandler), typeof(MethodPresenter));

    public event MethodInvokeRequestedRoutedEventHandler MethodInvokeRequested
    {
        add { AddHandler(MethodInvokeRequestedEvent, value); }
        remove { RemoveHandler(MethodInvokeRequestedEvent, value); }
    }
    ...
}

add a RoutedEvent for raising after the method has been invoked:

public class MethodInvokedRoutedEventArgs : RoutedEventArgs
{
    #region Constructors
    public MethodInvokedRoutedEventArgs()
    {
    }

    public MethodInvokedRoutedEventArgs(RoutedEvent routedEvent)
        : base(routedEvent)
    {
    }

    public MethodInvokedRoutedEventArgs(RoutedEvent routedEvent, object source)
        : base(routedEvent, source)
    {
    }
    #endregion

    public MethodInfo MethodInformation { get; set; }
    public MethodResultViewModel MethodResult { get; set; }
}

public delegate void MethodInvokedRoutedEventHandler
    (object sender, MethodInvokedRoutedEventArgs e);

public class MethodPresenter : Control
{
    ...
    public static readonly RoutedEvent MethodInvokedEvent =
        EventManager.RegisterRoutedEvent("MethodInvoked", RoutingStrategy.Bubble,
            typeof(MethodInvokedRoutedEventHandler), typeof(MethodPresenter));

    public event MethodInvokedRoutedEventHandler MethodInvoked
    {
        add { AddHandler(MethodInvokedEvent, value); }
        remove { RemoveHandler(MethodInvokedEvent, value); }
    }
    ...
}

and, add a property for indicating a method's invoke process:

public bool IsInvoking
{
    get { return (bool)GetValue(IsInvokingProperty); }
    protected set { SetValue(IsInvokingProperty, value); }
}

public static readonly DependencyProperty IsInvokingProperty =
    DependencyProperty.Register("IsInvoking", typeof(bool),
        typeof(MethodPresenter), new UIPropertyMetadata(false));

We raise the MethodInvokeRequested and MethodInvoked events and set the IsInvoking property, in the InvokeMethod method appropriately.

For stoping a method's invoke, we add a RoutedCommand:

private static RoutedCommand _stopInvokeMethodCommand;
public static RoutedCommand StopInvokeMethodCommand
{
    get
    {
        return _stopInvokeMethodCommand ??
            (_stopInvokeMethodCommand = new RoutedCommand
        ("StopInvokeMethod", typeof(MethodPresenter)));
    }
}

and, implement it to abort the thread of the method's invoke:

private static void CanExecuteStopInvokeMethodCommand
        (object sender, CanExecuteRoutedEventArgs e)
{
    MethodPresenter mp = sender as MethodPresenter;
    if (mp == null)
    {
        return;
    }

    e.CanExecute = mp.IsInvoking;
}

private static void ExecuteStopInvokeMethodCommand
        (object sender, ExecutedRoutedEventArgs e)
{
    MethodPresenter mp = sender as MethodPresenter;
    if (mp == null)
    {
        return;
    }

    mp.StopInvokeMethod();
}

private void StopInvokeMethod()
{
    if (_invokeMethodThread != null)
    {
        _invokeMethodThread.Abort();
        _invokeMethodThread = null;
    }

    IsInvoking = false;
}

static MethodPresenter()
{
    ...
    CommandBinding stopInvokeMethodBinding = new CommandBinding(StopInvokeMethodCommand,
        ExecuteStopInvokeMethodCommand, CanExecuteStopInvokeMethodCommand);

    CommandManager.RegisterClassCommandBinding
    (typeof(MethodPresenter), stopInvokeMethodBinding);
}

In the InvokeMethod method, we handle the ThreadAbortException as the following:

private void InvokeMethod()
{
    ...
    bool isAborted = false;

    try
    {
        object retVal = mi.Invoke(obj, parameters);
        methodReturnValue = mi.ReturnType != typeof(void) ? 
            new OutputValueViewModel(retVal) : null;
    }
    catch (ThreadAbortException tae)
    {
        isAborted = true;
    }
    ...
    finally
    {
        if (!isAborted)
        {
            ...
        }
    }
    ...
}

MethodPresenter style

After we have the MethodPresenter, we create a default style for presenting this control:

<Style TargetType="{x:Type local:MethodPresenter}">
    <Setter Property="Background" Value="LightGreen" />
    <Setter Property="BorderBrush" Value="DarkGreen" />
    <Setter Property="BorderThickness" Value="2" />
    <Setter Property="Foreground" Value="Black" />
    <Setter Property="Padding" Value="15" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MethodPresenter}">
                <Grid>
                    <Border BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            CornerRadius="10"
                            Background="{TemplateBinding Background}"
                            Padding="{TemplateBinding Padding}">
                        <DockPanel>
                        </DockPanel>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

In the ControlTemplate, we put a TextBlock for the method's name:

<TextBlock Text="{TemplateBinding MethodName}"
           DockPanel.Dock="Top"
           FontSize="20"
           Foreground="DarkBlue"
           HorizontalAlignment="Center"
           Margin="5" />

an ItemsControl for the method's parameters:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
    <ItemsControl ItemsSource="{Binding MethodParameters, 
    RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</ScrollViewer>

a Button for invoking the method:

<Button Content="Invoke" DockPanel.Dock="Bottom"
        Command="{x:Static local:MethodPresenter.InvokeMethodCommand}"
        HorizontalAlignment="Center"
        Margin="5" />

a Grid for the method's results:

<DataTemplate x:Key="methodResultItemDataTemplate">
    ...
</DataTemplate>

<ControlTemplate TargetType="{x:Type local:MethodPresenter}">
    <ControlTemplate.Resources>
        <Storyboard x:Key="showResultsStoryboard">
            <DoubleAnimation Storyboard.TargetName="resultsBorder"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleX)"
                             To="1"
                             Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="resultsBorder"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleY)"
                             To="1"
                             Duration="0:0:0.2" />
        </Storyboard>
        <Storyboard x:Key="hideResultsStoryboard">
            <DoubleAnimation Storyboard.TargetName="resultsBorder"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleX)"
                             To="0"
                             Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="resultsBorder"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleY)"
                             To="0"
                             Duration="0:0:0.2" />
        </Storyboard>
        <Storyboard x:Key="showResultsRegionStoryboard">
            <DoubleAnimation Storyboard.TargetName="resultsRegion"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleX)"
                             To="1"
                             Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="resultsRegion"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleY)"
                             To="1"
                             Duration="0:0:0.2" />
        </Storyboard>
        <Storyboard x:Key="hideResultsRegionStoryboard">
            <DoubleAnimation Storyboard.TargetName="resultsRegion"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleX)"
                             To="0"
                             Duration="0:0:0.2" />
            <DoubleAnimation Storyboard.TargetName="resultsRegion"
                             Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).
                    (ScaleTransform.ScaleY)"
                             To="0"
                             Duration="0:0:0.2" />
        </Storyboard>
    </ControlTemplate.Resources>
    ...
        <Grid DockPanel.Dock="Bottom" x:Name="resultsRegion">
            <Grid.LayoutTransform>
                <ScaleTransform ScaleX="0" ScaleY="0" />
            </Grid.LayoutTransform>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                <Button x:Name="btnClear"
                        Style="{StaticResource removeButtonStyle}"
                        VerticalAlignment="Center"
                        Command="{x:Static local:MethodPresenter.
                    ClearMethodResultsCommand}"
                        ToolTip="Clear the results."
                        Margin="2,2,4,2"/>
                <ToggleButton x:Name="resultsToggle"
                              Style="{StaticResource expandButtonStyle}"
                              VerticalAlignment="Center"
                              ToolTip="Expand the results region." />
            </StackPanel>
            <Border x:Name="resultsBorder"
                    Grid.Row="1"
                    Margin="0,3,0,0"
                    BorderThickness="1"
                    CornerRadius="3"
                    BorderBrush="#CC000000"
                    Background="#44000000">
                <Border.LayoutTransform>
                    <ScaleTransform ScaleX="0" ScaleY="0" />
                </Border.LayoutTransform>
                <StackPanel Margin="5">
                    <TextBlock Text="Results:"
                               HorizontalAlignment="Left"
                               FontSize="14"
                               Margin="0,0,0,5"
                               Foreground="#EEFFFFFF"/>
                    <ScrollViewer MaxHeight="100" VerticalScrollBarVisibility="Auto">
                        <ItemsControl ItemsSource="{Binding MethodResults, 
            RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                      ItemTemplate=
                "{StaticResource methodResultItemDataTemplate}" />
                    </ScrollViewer>
                </StackPanel>
            </Border>
        </Grid>
    ...
    <ControlTemplate.Triggers>
        <Trigger SourceName="resultsToggle"
                 Property="IsChecked"
                 Value="True">
            <Setter TargetName="resultsToggle"
                    Property="ToolTip"
                    Value="Collapse the results region." />
            <Trigger.EnterActions>
                <BeginStoryboard Storyboard="{StaticResource showResultsStoryboard}"/>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard Storyboard="{StaticResource hideResultsStoryboard}" />
            </Trigger.ExitActions>
        </Trigger>
        <Trigger Property="HasResults"
                 Value="True">
            <Trigger.EnterActions>
                <BeginStoryboard Storyboard=
            "{StaticResource showResultsRegionStoryboard}"/>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard Storyboard=
            "{StaticResource hideResultsRegionStoryboard}" />
            </Trigger.ExitActions>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

and, a Border for indicating a method's invoke:

<ControlTemplate TargetType="{x:Type local:MethodPresenter}">
    ...
        <Border Name="invokeDisplay"
                CornerRadius="10"
                Background="#66000000"
                Visibility="Hidden">
            <Grid VerticalAlignment="Center"
                  HorizontalAlignment="Center" >
                <Border Background="#BB000000">
                    <Border.Effect>
                        <BlurEffect Radius="15" />
                    </Border.Effect>
                </Border>
                <StackPanel Margin="10">
                    <TextBlock Foreground="LightGreen"
                               FontSize="16"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Center"
                               Text="Invoking..." />
                    <Button Content="Stop"
                            Command="{x:Static local:MethodPresenter.
                    StopInvokeMethodCommand}"
                            HorizontalAlignment="Center"
                            Padding="2"
                            Margin="5" />
                </StackPanel>
            </Grid>
        </Border>
    ...
    <ControlTemplate.Triggers>
        <Trigger Property="IsInvoking" Value="True">
            <Setter TargetName="invokeDisplay"
                    Property="Visibility"
                    Value="Visible" />
        </Trigger>
        ...
    </ControlTemplate.Triggers>
</ControlTemplate>

Handling Interfaces

Presenting interfaces

After we have the MethodPresenter control, we can invoke the methods of a given interface. For that purpose, we create a control that presents an interface:

public class InterfacePresenter : Control
{
    static InterfacePresenter()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(InterfacePresenter),
            new FrameworkPropertyMetadata(typeof(InterfacePresenter)));
    }
}

To this control, we add a property for holding the object on which to invoke the method:

public object ObjectInstance
{
    get { return (object)GetValue(ObjectInstanceProperty); }
    set { SetValue(ObjectInstanceProperty, value); }
}

public static readonly DependencyProperty ObjectInstanceProperty =
    DependencyProperty.Register("ObjectInstance", typeof(object),
        typeof(InterfacePresenter), new UIPropertyMetadata(null));

and, properties to support derived types and additional data-templates:

#region KnownTypes
public IEnumerable<Type> KnownTypes
{
    get { return (IEnumerable<Type>)GetValue(KnownTypesProperty); }
    set { SetValue(KnownTypesProperty, value); }
}

public static readonly DependencyProperty KnownTypesProperty =
    DependencyProperty.Register("KnownTypes", typeof(IEnumerable<Type>),
        typeof(InterfacePresenter), new UIPropertyMetadata(null));
#endregion

#region AutoGenerateCompatibleTypes
public bool AutoGenerateCompatibleTypes
{
    get { return (bool)GetValue(AutoGenerateCompatibleTypesProperty); }
    set { SetValue(AutoGenerateCompatibleTypesProperty, value); }
}

public static readonly DependencyProperty AutoGenerateCompatibleTypesProperty =
    DependencyProperty.Register("AutoGenerateCompatibleTypes", typeof(bool),
        typeof(InterfacePresenter), new UIPropertyMetadata(true));
#endregion

#region DataTemplates
public IEnumerable<TypeDataTemplate> DataTemplates
{
    get { return (IEnumerable<TypeDataTemplate>)GetValue(DataTemplatesProperty); }
    set { SetValue(DataTemplatesProperty, value); }
}

public static readonly DependencyProperty DataTemplatesProperty =
    DependencyProperty.Register("DataTemplates", typeof(IEnumerable<TypeDataTemplate>),
        typeof(InterfacePresenter), new UIPropertyMetadata(null));
#endregion

For getting the interface's methods, we add a property for holding the interface's methods:

private ObservableCollection<MethodInfo> _interfaceMethods;
public ObservableCollection<MethodInfo> InterfaceMethods
{
    get
    {
        return _interfaceMethods ?? 
    (_interfaceMethods = new ObservableCollection<MethodInfo>());
    }
}

and, set the interface's methods according to a given interface's type:

public Type InterfaceType
{
    get { return (Type)GetValue(InterfaceTypeProperty); }
    set { SetValue(InterfaceTypeProperty, value); }
}

public static readonly DependencyProperty InterfaceTypeProperty =
    DependencyProperty.Register
    ("InterfaceType", typeof(Type), typeof(InterfacePresenter),
        new UIPropertyMetadata(null, 
    OnInterfaceTypeChanged), ValidateInterfaceTypeValue);

private static void OnInterfaceTypeChanged
    (DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    InterfacePresenter ip = sender as InterfacePresenter;
    if (ip == null)
    {
        return;
    }

    ip.InterfaceMethods.Clear();

    Type interfaceType = ip.InterfaceType;
    if (interfaceType != null)
    {
        GetInterfaceMethods(interfaceType).ForEach(mi => ip.InterfaceMethods.Add(mi));
    }
}

private static bool ValidateInterfaceTypeValue(object value)
{
    if (value == null)
    {
        return true;
    }

    Type interfaceType = value as Type;
    if (interfaceType != null && interfaceType.IsInterface)
    {
        return true;
    }

    return false;
}

private static List<MethodInfo> GetInterfaceMethods(Type interfaceType)
{
    List<MethodInfo> methodInfos = new List<MethodInfo>();

    AddInterfaceMethods(interfaceType, methodInfos);

    return methodInfos;
}

private static void AddInterfaceMethods
    (Type interfaceType, List<MethodInfo> methodInfos)
{
    foreach (MethodInfo mi in interfaceType.GetMethods())
    {
        methodInfos.Add(mi);
    }

    foreach (Type baseInterface in interfaceType.GetInterfaces())
    {
        AddInterfaceMethods(baseInterface, methodInfos);
    }
}

For presenting the method's result, we add a property for holding the current method's result:

public MethodResultViewModel CurrentMethodResult
{
    get { return (MethodResultViewModel)GetValue(CurrentMethodResultProperty); }
    set { SetValue(CurrentMethodResultProperty, value); }
}

public static readonly DependencyProperty CurrentMethodResultProperty =
    DependencyProperty.Register("CurrentMethodResult", typeof(MethodResultViewModel),
        typeof(InterfacePresenter), new UIPropertyMetadata
            (null, OnCurrentMethodResultChanged));

private static void OnCurrentMethodResultChanged
        (DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    InterfacePresenter ip = sender as InterfacePresenter;
    if (ip == null)
    {
        return;
    }

    ip.RaiseEvent(new RoutedEventArgs
    (InterfacePresenter.CurrentMethodResultChangedEvent));
}

public static readonly RoutedEvent CurrentMethodResultChangedEvent = 
                    EventManager.RegisterRoutedEvent(
    "CurrentMethodResultChanged", RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), typeof(InterfacePresenter));

public event RoutedEventHandler MethodInvoked
{
    add { AddHandler(CurrentMethodResultChangedEvent, value); }
    remove { RemoveHandler(CurrentMethodResultChangedEvent, value); }
}

and, handle the MethodInvoked event, to set the current result, to the result of the last invoked method:

public InterfacePresenter()
{
    AddHandler(MethodPresenter.MethodInvokedEvent, 
        new MethodInvokedRoutedEventHandler(OnMethodInvoked));
}

private void OnMethodInvoked(object sender, MethodInvokedRoutedEventArgs e)
{
    CurrentMethodResult = e.MethodResult;
}

InterfacePresenter style

After we have the InterfacePresenter, we create a default style for presenting this control:

<Style TargetType="{x:Type local:InterfacePresenter}">
    <Setter Property="Background" Value="LightYellow" />
    <Setter Property="BorderBrush" Value="Orange" />
    <Setter Property="BorderThickness" Value="2" />
    <Setter Property="Padding" Value="10" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:InterfacePresenter}">
                <DockPanel>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

In the ControlTemplate, we put a Border for presenting the interface's methods:

<Border BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        Background="{TemplateBinding Background}"
        CornerRadius="15"
        Padding="{TemplateBinding Padding}"
        Margin="5">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="Methods:"
                   Foreground="DarkGreen"
                   FontSize="14"
                   FontWeight="Bold"
                   Margin="0,0,0,5" />
        <ScrollViewer Grid.Row="1"
                      VerticalScrollBarVisibility="Auto"
                      Margin="0,5,0,0">
            <ItemsControl ItemsSource="{Binding InterfaceMethods, 
        RelativeSource={RelativeSource Mode=TemplatedParent}}"  >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:MethodPresenter MethodInformation="{Binding}"
                                               ObjectInstance="{Binding DataContext.
                        ObjectInstance, 
                        ElementName=templatedParentHolder}"
                                               StoreMethodResults="{Binding DataContext.
                        StoreMethodResults, 
                        ElementName=templatedParentHolder}"
                                               KnownTypes="{Binding DataContext.
                        KnownTypes, 
                        ElementName=templatedParentHolder}"
                                               AutoGenerateCompatibleTypes=
                        "{Binding DataContext.
                        AutoGenerateCompatibleTypes, 
                        ElementName=templatedParentHolder}"
                                               DataTemplates="{Binding DataContext.
                        DataTemplates, 
                        ElementName=templatedParentHolder}"
                                               Margin="5"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
        <FrameworkElement x:Name="templatedParentHolder"
                          DataContext="{Binding RelativeSource=
                {RelativeSource Mode=TemplatedParent}}"
                          Visibility="Collapsed" />
    </Grid>
</Border>

and, a Border for presenting the current result:

<DataTemplate x:Key="methodResultDataTemplate">
    ...
</DataTemplate>

<ControlTemplate TargetType="{x:Type local:InterfacePresenter}">
    ...
    <Border x:Name="currentMethodResultRegion"
            DockPanel.Dock="Bottom"
            Background="{TemplateBinding Background}"
            BorderThickness="{TemplateBinding BorderThickness}"
            CornerRadius="15"
            Padding="{TemplateBinding Padding}"
            Margin="5"
            RenderTransformOrigin="0.5,0.5">
        <Border.BorderBrush>
            <SolidColorBrush Color="Orange" />
        </Border.BorderBrush>
        <Border.RenderTransform>
            <ScaleTransform />
        </Border.RenderTransform>
        <ScrollViewer HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto">
            <ContentControl Content="{Binding CurrentMethodResult, 
        RelativeSource={RelativeSource Mode=TemplatedParent}}"
                    ContentTemplate="{StaticResource methodResultDataTemplate}" />
        </ScrollViewer>
    </Border>
    ...
</ControlTemplate>

Handling Objects

Presenting object's properties

In order to support invoking methods that are affected from the object's properties, we need a way to set the object's properties, before the invoke of the method. For that purpose, we create a control that presents object's properties:

public class ValuePresenter : Control
{
    static ValuePresenter()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ValuePresenter), 
            new FrameworkPropertyMetadata(typeof(ValuePresenter)));
    }
}

To this control, we add properties for the value and the value's type:

#region Value
public object Value
{
    get { return (object)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(object),
        typeof(ValuePresenter), new UIPropertyMetadata(null));
#endregion

#region ValueType
public Type ValueType
{
    get { return (Type)GetValue(ValueTypeProperty); }
    set { SetValue(ValueTypeProperty, value); }
}

public static readonly DependencyProperty ValueTypeProperty =
    DependencyProperty.Register("ValueType", typeof(Type),
        typeof(ValuePresenter), new UIPropertyMetadata(null));
#endregion

and, add a property for holding a view-model for the value:

public InputValueViewModel PresentedValue
{
    get { return (InputValueViewModel)GetValue(PresentedValueProperty); }
    protected set { SetValue(PresentedValueProperty, value); }
}

public static readonly DependencyProperty PresentedValueProperty =
    DependencyProperty.Register("PresentedValue", typeof(InputValueViewModel),
        typeof(ValuePresenter), new UIPropertyMetadata(null));

When the Value property is changed, we change the value of the value's view-model and vice versa.

In order to present the value that is set to a ValuePresenter, we have to update each sub view-model of the view-model according to the set value. We can do that as the following:

protected void UpdateValue(object value)
{
    object newValue = value;

    IsNull = newValue == null;

    if (newValue == null)
    {
        SubFields.Clear();
        CollectionElements.Clear();

        return;
    }

    if (SelectedCompatibleType == null || IsCollection || HasSubFields)
    {
        Type newValueType = newValue != null ? newValue.GetType() : null;

        if (ValueType == null)
        {
            ValueType = newValueType;
        }
        else if (SelectedCompatibleType != newValueType)
        {
            SelectedCompatibleType = newValueType;
        }
    }

    if (HasSubFields || IsCollection)
    {
        UpdateSubFields(newValue);

        UpdateCollectionElements(newValue);
    }
}

private void UpdateCollectionElements(object newValue)
{
    IEnumerable newCollectionValue = newValue as IEnumerable;
    if (!IsCollection || newCollectionValue == null)
    {
        return;
    }

    int elementIndex = 0;
    foreach (object val in newCollectionValue)
    {
        if (elementIndex >= CollectionElements.Count)
        {
            AddNewCollectionElement(false);
        }

        ValueViewModel currElement = CollectionElements[elementIndex];
        if (currElement != null)
        {
            currElement.Value = val;
        }

        elementIndex++;
    }

    // Remove surpluses elements.
    while (CollectionElements.Count > elementIndex)
    {
        CollectionElements.RemoveAt(elementIndex);
    }
}

private void UpdateSubFields(object newValue)
{
    if (newValue == null)
    {
        return;
    }

    Type newValueType = newValue.GetType();

    foreach (ValueViewModel subField in SubFields)
    {
        if (subField is PropertyInputValueViewModel)
        {
            PropertyInfo pi = newValueType.GetProperty(subField.Name);
            if (pi != null)
            {
                subField.Value = pi.GetValue(newValue, null);
            }
        }
        else if (subField is FieldInputValueViewModel)
        {
            FieldInfo fi = newValueType.GetField(subField.Name);
            if (fi != null)
            {
                subField.Value = fi.GetValue(newValue);
            }
        }
    }
}

In the UpdateValue method, we change the SelectedCompatibleType if it is needed and, update the sub view-models of the view-model appropriately.

The default style of the ValuePresenter control is defined as the following:

<Style TargetType="{x:Type local:ValuePresenter}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ValuePresenter}">
                <ScrollViewer VerticalScrollBarVisibility="Auto">
                    <ContentControl Content="{TemplateBinding PresentedValue}" />
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Presenting objects

For presenting objects, we create a control:

public class ObjectPresenter : Control
{
    static ObjectPresenter()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ObjectPresenter),
            new FrameworkPropertyMetadata(typeof(ObjectPresenter)));
    }
}

To this control, we add property for holding the object that we want to present:

public object ObjectInstance
{
    get { return (object)GetValue(ObjectInstanceProperty); }
    set { SetValue(ObjectInstanceProperty, value); }
}

public static readonly DependencyProperty ObjectInstanceProperty =
    DependencyProperty.Register("ObjectInstance", 
        typeof(object), typeof(ObjectPresenter),
        new UIPropertyMetadata(null, OnObjectInstanceChanged));

private static void OnObjectInstanceChanged
    (DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
}

and, properties to support derived types and additional data-templates:

#region KnownTypes
public IEnumerable<Type> KnownTypes
{
    get { return (IEnumerable<Type>)GetValue(KnownTypesProperty); }
    set { SetValue(KnownTypesProperty, value); }
}

public static readonly DependencyProperty KnownTypesProperty =
    DependencyProperty.Register("KnownTypes", typeof(IEnumerable<Type>),
        typeof(ObjectPresenter), new UIPropertyMetadata(null));
#endregion

#region AutoGenerateCompatibleTypes
public bool AutoGenerateCompatibleTypes
{
    get { return (bool)GetValue(AutoGenerateCompatibleTypesProperty); }
    set { SetValue(AutoGenerateCompatibleTypesProperty, value); }
}

public static readonly DependencyProperty AutoGenerateCompatibleTypesProperty =
    DependencyProperty.Register("AutoGenerateCompatibleTypes", typeof(bool),
        typeof(ObjectPresenter), new UIPropertyMetadata(true));
#endregion

#region DataTemplates
public IEnumerable<TypeDataTemplate> DataTemplates
{
    get { return (IEnumerable<TypeDataTemplate>)GetValue(DataTemplatesProperty); }
    set { SetValue(DataTemplatesProperty, value); }
}

public static readonly DependencyProperty DataTemplatesProperty =
    DependencyProperty.Register("DataTemplates", typeof(IEnumerable<TypeDataTemplate>),
        typeof(ObjectPresenter), new UIPropertyMetadata(null));
#endregion

The default style of the ObjectPresenter control is defined as the following:

<Style TargetType="{x:Type local:ObjectPresenter}">
    <Setter Property="Background" Value="LightYellow" />
    <Setter Property="BorderBrush" Value="Orange" />
    <Setter Property="BorderThickness" Value="2" />
    <Setter Property="Padding" Value="10" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ObjectPresenter}">
                <DockPanel>
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="15"
                            Padding="{TemplateBinding Padding}"
                            Margin="5"
                            DockPanel.Dock="Right">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <TextBlock Text="Fields:"
                                       Foreground="DarkGreen"
                                       FontSize="14"
                                       FontWeight="Bold"
                                       Margin="0,0,0,5" />
                            <ScrollViewer Margin="0,5,0,0"
                                          VerticalScrollBarVisibility="Auto"
                                          Grid.Row="1">
                                <local:ValuePresenter Value="{Binding ObjectInstance, 
                        RelativeSource={RelativeSource 
                        Mode=TemplatedParent}, Mode=TwoWay}"
                                                         KnownTypes="{TemplateBinding 
                            KnownTypes}"
                                                         AutoGenerateCompatibleTypes=
                        "{TemplateBinding 
                        AutoGenerateCompatibleTypes}"
                                                         DataTemplates="{TemplateBinding 
                        DataTemplates}" />
                            </ScrollViewer>
                        </Grid>
                    </Border>
                    <local:InterfacePresenter Background="{TemplateBinding Background}"
                                              BorderBrush="{TemplateBinding BorderBrush}"
                                              BorderThickness="{TemplateBinding 
                        BorderThickness}"
                                              Padding="{TemplateBinding Padding}"
                                              InterfaceType=
                        "{TemplateBinding InterfaceType}"
                                              ObjectInstance="{TemplateBinding 
                        ObjectInstance}"
                                              StoreMethodResults="{TemplateBinding 
                        StoreMethodResults}"
                                              KnownTypes="{TemplateBinding KnownTypes}"
                                              AutoGenerateCompatibleTypes=
                    "{TemplateBinding 
                        AutoGenerateCompatibleTypes}"
                                              DataTemplates=
                    "{TemplateBinding DataTemplates}" />
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

In this style, we use the InterfacePresenter control to present the object's methods and, the ValuePresenter control to present the object's fields.

For setting the object's methods in the methods' collection of the InterfacePresenter, we add a TemplatePart:

[TemplatePart(Name = "PART_InterfacePresenter", Type = typeof(InterfacePresenter))]
public class ObjectPresenter : Control
{
    ...
}

set the name of the InterfacePresenter to the name of the TemplatePart:

<local:InterfacePresenter x:Name="PART_InterfacePresenter"
                            ...
                             />

find the InterfacePresenter according to the name:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    _interfacePresenter = GetTemplateChild("PART_InterfacePresenter") 
                as InterfacePresenter;
}

private InterfacePresenter _interfacePresenter;

and, update the methods' collection of the InterfacePresenter, for each time the ObjectInstance is changed:

private static void OnObjectInstanceChanged
    (DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    ObjectPresenter op = sender as ObjectPresenter;
    if (op == null)
    {
        return;
    }

    if (e.OldValue != null && e.NewValue != null &&
        e.OldValue.GetType() == e.NewValue.GetType())
    {
        // The new object has the same type as the previous, 
        // so we don't have to regenerate methods.
        return;
    }

    if (op.InterfaceType == null)
    {
        op.UpdateInterfacePresenterWithObjectInstanceMethods();
    }
}

private void UpdateInterfacePresenterWithObjectInstanceMethods()
{
    if (_interfacePresenter == null)
    {
        return;
    }

    _interfacePresenter.InterfaceMethods.Clear();

    if (ObjectInstance == null)
    {
        return;
    }

    IEnumerable<methodinfo /> methods = ObjectInstance.GetType().GetMethods();
    foreach (MethodInfo mi in methods)
    {
        if (mi.IsSpecialName)
        {
            continue;
        }

        _interfacePresenter.InterfaceMethods.Add(mi);
    }
}

How to Use It

Using ValuePresenter

For demonstrating the use of the ValuePresenter control, we create a window that enables editing properties of a shape.

For that window, we create a base class for holding a shape:

public abstract class MyShape
{
    public MyShape()
    {
        Left = 10;
        Top = 10;
        FillColor = Colors.LightGreen;
        StrokeColor = Colors.Green;
        StrokeThickness = 2;
    }

    public double Left { get; set; }
    public double Top { get; set; }
    public Color FillColor { get; set; }
    public Color StrokeColor { get; set; }
    public double StrokeThickness { get; set; }
}

create derived classes for holding rectangle and circle:

public class MyRectangle : MyShape
{
    public MyRectangle()
    {
        Width = 150;
        Height = 200;
    }

    public double Width { get; set; }
    public double Height { get; set; }
}

public class MyCircle : MyShape
{
    public MyCircle()
    {
        Diameter = 100.0;
    }

    public double Diameter { get; set; }
}

create data-templates for presenting the shapes:

<DataTemplate DataType="{x:Type local:MyRectangle}">
    <Canvas>
        <Rectangle Canvas.Top="{Binding Top}"
                Canvas.Left="{Binding Left}"
                StrokeThickness="{Binding StrokeThickness}"
                Width="{Binding Width}"
                Height="{Binding Height}" >
            <Rectangle.Fill>
                <SolidColorBrush Color="{Binding FillColor}" />
            </Rectangle.Fill>
            <Rectangle.Stroke>
                <SolidColorBrush Color="{Binding StrokeColor}" />
            </Rectangle.Stroke>
        </Rectangle>
    </Canvas>
</DataTemplate>

<DataTemplate DataType="{x:Type local:MyCircle}">
    <Canvas>
        <Ellipse Canvas.Top="{Binding Top}"
                Canvas.Left="{Binding Left}"
                StrokeThickness="{Binding StrokeThickness}"
                Width="{Binding Diameter}"
                Height="{Binding Diameter}" >
            <Ellipse.Fill>
                <SolidColorBrush Color="{Binding FillColor}" />
            </Ellipse.Fill>
            <Ellipse.Stroke>
                <SolidColorBrush Color="{Binding StrokeColor}" />
            </Ellipse.Stroke>
        </Ellipse>
    </Canvas>
</DataTemplate>

add a ValuePresenter for editing shape's properties:

<Border Grid.Column="1"
        BorderBrush="Blue"
        BorderThickness="2"
        Background="LightBlue"
        CornerRadius="5"
        Margin="5" Padding="5">
    <ObjectPresentation:ValuePresenter x:Name="vp"
                                       ValueType="{x:Type local:MyShape}" />
</Border>

and, add a ContentControl for presenting the ValuePresenter's value (the shape):

<ContentControl Name="myContent"
                Content="{Binding ElementName=vp, Path=Value, Mode=TwoWay}" />

For presenting colors, we create a UserControl:

<UserControl x:Class="ObjectPresentation.Examples.Client.ColorPicker"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TextBlock Text="R:" VerticalAlignment="Center" />
        <Slider Name="rVal" Minimum="0" Maximum="255" Value="60" 
        Grid.Column="1" Margin="0,2" ValueChanged="OnValueChanged" />
        <TextBlock Text="G:" Grid.Row="1" VerticalAlignment="Center"/>
        <Slider Name="gVal" Minimum="0" Maximum="255" Value="60" 
        Grid.Column="1" Grid.Row="1"  Margin="0,2" 
        ValueChanged="OnValueChanged" />
        <TextBlock Text="B:" Grid.Row="2" VerticalAlignment="Center" />
        <Slider Name="bVal" Minimum="0" Maximum="255" Value="60" 
        Grid.Column="1" Grid.Row="2" Margin="0,2" 
        ValueChanged="OnValueChanged" />
        <TextBlock Text="A:" Grid.Row="3" VerticalAlignment="Center" />
        <Slider Name="aVal" Minimum="0" Maximum="255" Value="60" 
        Grid.Column="1" Grid.Row="3" Margin="0,2" 
        ValueChanged="OnValueChanged" />
        <Grid Grid.Column="2" Grid.RowSpan="4" Margin="5" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Rectangle>
                <Rectangle.Fill>
                    <LinearGradientBrush SpreadMethod="Repeat" StartPoint="0.5,0" 
            EndPoint="0.5,0.2"  >
                        <GradientStop Color="White" Offset="0" />
                        <GradientStop Color="White" Offset="0.5" />
                        <GradientStop Color="LightGray" Offset="0.5" />
                        <GradientStop Color="LightGray" Offset="1" />
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Grid.Column="1">
                <Rectangle.Fill>
                    <LinearGradientBrush SpreadMethod="Repeat" StartPoint="0.5,0" 
            EndPoint="0.5,0.2"  >
                        <GradientStop Color="LightGray" Offset="0" />
                        <GradientStop Color="LightGray" Offset="0.5" />
                        <GradientStop Color="White" Offset="0.5" />
                        <GradientStop Color="White" Offset="1" />
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle Grid.ColumnSpan="2"
                       Stroke="Black"
                       StrokeThickness="1"
                       Width="16" >
                <Rectangle.Fill>
                    <SolidColorBrush Color="{Binding RelativeSource=
            {RelativeSource Mode=FindAncestor, 
            AncestorType={x:Type UserControl}}, Path=SelectedColor}" />
                </Rectangle.Fill>
            </Rectangle>
        </Grid>
    </Grid>
</UserControl>

 

public partial class ColorPicker : UserControl
{
    public ColorPicker()
    {
        InitializeComponent();

        rVal.Value = SelectedColor.R;
        gVal.Value = SelectedColor.G;
        bVal.Value = SelectedColor.B;
        aVal.Value = SelectedColor.A;
    }

    #region SelectedColor
    public Color SelectedColor
    {
        get { return (Color)GetValue(SelectedColorProperty); }
        set { SetValue(SelectedColorProperty, value); }
    }

    public static readonly DependencyProperty SelectedColorProperty =
        DependencyProperty.Register("SelectedColor", typeof(Color), 
    typeof(ColorPicker), new UIPropertyMetadata(Colors.Transparent, 
    OnSelectedColorChanged));

    private static void OnSelectedColorChanged(DependencyObject sender, 
        DependencyPropertyChangedEventArgs e)
    {
        ColorPicker cp = sender as ColorPicker;
        if (cp == null)
        {
            return;
        }

        Color newColor = cp.SelectedColor;

        cp.rVal.Value = newColor.R;
        cp.gVal.Value = newColor.G;
        cp.bVal.Value = newColor.B;
        cp.aVal.Value = newColor.A;

    }
    #endregion

    private void OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double /> e)
    {
        if (!IsLoaded)
        {
            return;
        }

        SelectedColor = new Color
        {
            R = (byte)rVal.Value,
            G = (byte)gVal.Value,
            B = (byte)bVal.Value,
            A = (byte)aVal.Value
        };
    }
}

create a data-template that uses this UserControl for the Color type:

<Grid.Resources>
    <x:ArrayExtension x:Key="typeDataTemplates" 
    Type="{x:Type ObjectPresentation:TypeDataTemplate}">
        <ObjectPresentation:TypeDataTemplate ValueType="{x:Type Color}">
            <ObjectPresentation:TypeDataTemplate.ValueViewModelDataTemplate>
                <DataTemplate>
                    <local:ColorPicker Width="150"
                                       SelectedColor="{Binding Path=Value, 
            Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                </DataTemplate>
            </ObjectPresentation:TypeDataTemplate.ValueViewModelDataTemplate>
        </ObjectPresentation:TypeDataTemplate>
    </x:ArrayExtension>
</Grid.Resources>

and, set the data-template in the ValuePresenter:

<ObjectPresentation:ValuePresenter x:Name="vp"
                                   ValueType="{x:Type local:MyShape}"
                                   DataTemplates="{StaticResource typeDataTemplates}" />

The result can be shown as the following:

ValuePresenter example

Using MethodPresenter

For demonstrating the use of the MethodPresenter control, we create a window that enables adding shapes to a Canvas.

For that window, we add a Canvas for holding the shapes:

<Canvas Name="myCanvas" />

add a MethodPresenter:

<ObjectPresentation:MethodPresenter Name="myMethodPresenter"
                                    Grid.Column="1"
                                    DataTemplates="{StaticResource typeDataTemplates}"
                                    Margin="5" />

add a method for adding shape (using the shape that we created in the previous example):

public void AddShape(MyShape shape)
{
    if (shape == null)
    {
        return;
    }

    Dispatcher.BeginInvoke(DispatcherPriority.Normal,
        new ThreadStart(() =>
        {

            Shape element = null;

            if (shape is MyCircle)
            {
                MyCircle mc = shape as MyCircle;
                element = new Ellipse
                {
                    Width = mc.Diameter,
                    Height = mc.Diameter
                };
            }
            else if (shape is MyRectangle)
            {
                MyRectangle mr = shape as MyRectangle;
                element = new Rectangle
                {
                    Width = mr.Width,
                    Height = mr.Height,
                };
            }

            if (element != null)
            {
                element.Fill = new SolidColorBrush(shape.FillColor);
                element.Stroke = new SolidColorBrush(shape.StrokeColor);
                element.StrokeThickness = shape.StrokeThickness;

                Canvas.SetLeft(element, shape.Left);
                Canvas.SetTop(element, shape.Top);

                myCanvas.Children.Add(element);
            }
        }));
}

and, set the MethodInformation of the MethodPresenter to that method:

public MethodPresenterExample()
{
    InitializeComponent();

    myMethodPresenter.MethodInformation = this.GetType().GetMethod("AddShape");
    myMethodPresenter.ObjectInstance = this;
}

The result can be shown as the following:

MethodPresenter example

Using InterfacePresenter

For demonstrating the use of the InterfacePresenter control, we create a WCF service and a GUI client that calls the service's methods.

For the service, we create some data-contracts:

[DataContract]
public class Person
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int Age { get; set; }

    [DataMember]
    public PersonGender Gender { get; set; }

    [DataMember]
    public Address Address { get; set; }

    [DataMember]
    public List<Person> Children { get; set; }
}

[DataContract]
public enum PersonGender
{
    [EnumMember]
    Male,

    [EnumMember]
    Female
}

[DataContract]
public class Address
{
    [DataMember]
    public string Country { get; set; }

    [DataMember]
    public string City { get; set; }

    [DataMember]
    public string Street { get; set; }

    [DataMember]
    public int Number { get; set; }
}

create a service-contract:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    void AddPerson(Person person);

    [OperationContract]
    Person GetPersonByName(string name);

    [OperationContract]
    Person[] GetAllPersons();

    [OperationContract]
    double Add(double a, double b);

    [OperationContract]
    double Sub(double a, double b);

    [OperationContract]
    double Mul(double a, double b);

    [OperationContract]
    double Div(double a, double b);
}

create a class that implements the service-contract:

public class MyService : IMyService
{
    private List<Person> _persons;

    public MyService()
    {
        _persons = new List<Person>();
    }

    public void AddPerson(Person person)
    {
        if (person != null)
        {
            _persons.Add(person);
        }
    }

    public Person GetPersonByName(string name)
    {
        return _persons.FirstOrDefault(p => p.Name == name);
    }

    public Person[] GetAllPersons()
    {
        return _persons.ToArray();
    }

    public double Add(double a, double b)
    {
        return a + b;
    }

    public double Sub(double a, double b)
    {
        return a - b;
    }

    public double Mul(double a, double b)
    {
        return a * b;
    }

    public double Div(double a, double b)
    {
        return a / b;
    }
}

add configuration for the service:

<system.serviceModel>
  <services>
    <service name="ObjectPresentation.Examples.Service.MyService">
      <endpoint address="net.tcp://localhost:8123/MyService"
                binding="netTcpBinding"
                contract="ObjectPresentation.Examples.Contracts.IMyService" />
    </service>
  </services>
</system.serviceModel>

and, create a ServiceHost using the service's class:

ServiceHost host = new ServiceHost(typeof(MyService));
host.Open();

Console.WriteLine("Press <Enter> to stop.");
Console.ReadLine();

host.Close();

For showing the inputs that arrive to the service's methods and the results, we can use my LoggingBehavior as the following:

[LoggingBehavior]
public class MyService : IMyService
{
    ...
}

For the client, we add configuration for the service:

<system.serviceModel>
  <client>
    <endpoint address="net.tcp://localhost:8123/MyService"
              binding="netTcpBinding"
              contract="ObjectPresentation.Examples.Contracts.IMyService"
              name="myServiceEp" />
  </client>
</system.serviceModel>

add an InterfacePresenter:

<ObjectPresentation:InterfacePresenter x:Name="myInterfacePresenter" />

and, set its ObjectInstance to the service's proxy:

public InterfacePresenterExample()
{
    InitializeComponent();

    ChannelFactory<IMyService> factory = new ChannelFactory<IMyService>("myServiceEp");
    IMyService proxy = factory.CreateChannel();

    myInterfacePresenter.InterfaceType = typeof(IMyService);
    myInterfacePresenter.ObjectInstance = proxy;
}

The result can be shown as the following:

InterfacePresenter example - client
InterfacePresenter example - server

Using ObjectPresenter

For demonstrating the use of the ObjectPresenter control, we add some methods to the Person class:

[DataContract]
public class Person
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int Age { get; set; }

    [DataMember]
    public PersonGender Gender { get; set; }

    [DataMember]
    public Address Address { get; set; }

    [DataMember]
    public List<Person> Children { get; set; }

    public string[] GetChildrenNames()
    {
        if (Children == null)
        {
            return null;
        }

        return Children.Select(c => c.Name).ToArray();
    }

    public Person GetChildByName(string name)
    {
        // Throws exception when Children==null.

        return Children.FirstOrDefault(c => c.Name == name);
    }

    public void AddChild(Person child)
    {
        if (Children == null)
        {
            Children = new List<Person>();
        }

        Children.Add(child);
    }

    public void SetAddress(Address address)
    {
        this.Address = address;
    }
}

add an ObjectPresenter:

<ObjectPresentation:ObjectPresenter Name="myObjectPresenter" />

and, set its ObjectInstance to a Person object:

public ObjectPresenterExample()
{
    InitializeComponent();

    myObjectPresenter.ObjectInstance = new Person();
}

The result can be shown as the following:

ObjectPresenter example

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shmuel Zang
Software Developer
Israel Israel
No Biography provided

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
shekexi10-May-12 18:39
membershekexi10-May-12 18:39 
GeneralRe: My vote of 5 Pin
Shmuel Zang10-May-12 19:36
memberShmuel Zang10-May-12 19:36 
QuestionI think this is probably great... Pin
mark merrens8-May-12 10:38
membermark merrens8-May-12 10:38 
AnswerRe: I think this is probably great... Pin
Paulo Zemek9-May-12 3:01
mvpPaulo Zemek9-May-12 3:01 
AnswerRe: I think this is probably great... Pin
Shmuel Zang10-May-12 19:34
memberShmuel Zang10-May-12 19:34 
Questionvery nice Pin
CIDev13-Feb-12 10:06
memberCIDev13-Feb-12 10:06 
AnswerRe: very nice Pin
Shmuel Zang14-Feb-12 9:16
memberShmuel Zang14-Feb-12 9:16 
QuestionMy vote of 5 Pin
Bassam Saoud10-Feb-12 9:37
memberBassam Saoud10-Feb-12 9:37 
AnswerRe: My vote of 5 Pin
Shmuel Zang11-Feb-12 10:49
memberShmuel Zang11-Feb-12 10:49 
QuestionEr I think I would just use NUnit for this right Pin
Sacha Barber10-Feb-12 4:59
mvpSacha Barber10-Feb-12 4:59 
AnswerRe: Er I think I would just use NUnit for this right Pin
Shmuel Zang11-Feb-12 10:47
memberShmuel Zang11-Feb-12 10:47 
GeneralRe: Er I think I would just use NUnit for this right Pin
Sacha Barber14-Feb-12 0:29
mvpSacha Barber14-Feb-12 0:29 
GeneralRe: Er I think I would just use NUnit for this right Pin
Dave Cross10-May-12 22:28
memberDave Cross10-May-12 22:28 
GeneralMy vote of 5 Pin
Armando Airo'15-Jan-12 22:26
memberArmando Airo'15-Jan-12 22:26 
GeneralRe: My vote of 5 Pin
Shmuel Zang16-Jan-12 7:34
memberShmuel Zang16-Jan-12 7:34 
GeneralMy vote of 5 Pin
Filip D'haene6-Jan-12 8:47
memberFilip D'haene6-Jan-12 8:47 
GeneralRe: My vote of 5 Pin
Shmuel Zang7-Jan-12 8:24
memberShmuel Zang7-Jan-12 8:24 
GeneralMy vote of 5 Pin
Marc Clifton6-Jan-12 3:14
protectorMarc Clifton6-Jan-12 3:14 
GeneralRe: My vote of 5 Pin
Shmuel Zang7-Jan-12 8:22
memberShmuel Zang7-Jan-12 8:22 
GeneralMy vote of 5 Pin
Pete O'Hanlon6-Jan-12 0:25
protectorPete O'Hanlon6-Jan-12 0:25 
GeneralRe: My vote of 5 Pin
Shmuel Zang7-Jan-12 8:21
memberShmuel Zang7-Jan-12 8:21 
GeneralMy vote of 5 Pin
Vllado25-Jan-12 21:04
memberVllado25-Jan-12 21:04 
GeneralRe: My vote of 5 Pin
Shmuel Zang7-Jan-12 8:20
memberShmuel Zang7-Jan-12 8:20 
Generalgood job Pin
sphix5-Jan-12 12:49
membersphix5-Jan-12 12:49 
GeneralRe: good job Pin
Shmuel Zang7-Jan-12 8:19
memberShmuel Zang7-Jan-12 8:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150728.1 | Last Updated 5 Jan 2012
Article Copyright 2012 by Shmuel Zang
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid