Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

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

, 5 Jan 2012 CPOL
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.
ObjectPresentation_Demo.zip
ObjectPresentation_Demo
Client
ObjectPresentation.dll
ObjectPresentation.Examples.Client.exe
ObjectPresentation.Examples.Client.pdb
ObjectPresentation.Examples.Contracts.dll
ObjectPresentation.Examples.Contracts.pdb
ObjectPresentation.pdb
Server
ObjectPresentation.Examples.Contracts.dll
ObjectPresentation.Examples.Contracts.pdb
ObjectPresentation.Examples.Server.exe
ObjectPresentation.Examples.Server.pdb
ObjectPresentation.Examples.Service.dll
ObjectPresentation.Examples.Service.pdb
WcfLogPrints.dll
ObjectPresentation_src.zip
ObjectPresentation_src
ObjectPresentation
bin
Debug
Release
obj
Debug
Properties
Settings.settings
Styles
Themes
ObjectPresentation.Examples.Client
bin
Debug
Release
obj
x86
Debug
Properties
Settings.settings
ObjectPresentation.Examples.Contracts
bin
Debug
Release
obj
Debug
Properties
ObjectPresentation.Examples.Server
bin
Debug
Release
obj
x86
Debug
Properties
ObjectPresentation.Examples.Service
bin
Debug
obj
Debug
Properties
WcfLogPrints.dll
ObjectPresentation.suo
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Threading;
using System.Windows.Threading;

namespace ObjectPresentation
{
    public class MethodPresenter : Control
    {
        #region Properties
        
        #region MethodInformation
        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;
            }
        }
        #endregion

        #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

        #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

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

        #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 IsInvoking
        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));
        #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

        #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, OnKnownTypesChanged));

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

            foreach (InputValueViewModel ivvm in mp.MethodParameters)
            {
                ivvm.KnownTypes = mp.KnownTypes;
            }
        }
        #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, OnAutoGenerateCompatibleTypesChanged));

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

            foreach (InputValueViewModel ivvm in mp.MethodParameters)
            {
                ivvm.AutoGenerateCompatibleTypes = mp.AutoGenerateCompatibleTypes;
            }
        }
        #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, OnDataTemplatesChanged));

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

            foreach (InputValueViewModel ivvm in mp.MethodParameters)
            {
                ivvm.DataTemplates = mp.DataTemplates;
            }
        }
        #endregion

        #endregion

        #region Events

        #region MethodInvokeRequested
        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); }
        }
        #endregion

        #region MethodInvoked
        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); }
        }
        #endregion

        #region MethodResultShown
        public static readonly RoutedEvent MethodResultShownEvent = EventManager.RegisterRoutedEvent(
           "MethodResultShown", RoutingStrategy.Bubble, typeof(MethodResultShownRoutedEventHandler), typeof(MethodPresenter));

        public event MethodResultShownRoutedEventHandler MethodResultShown
        {
            add { AddHandler(MethodResultShownEvent, value); }
            remove { RemoveHandler(MethodResultShownEvent, value); }
        }
        #endregion

        #endregion

        #region Commands

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

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

            IsInvoking = true;

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

            // Raise MethodInvokeRequested event.
            MethodInvokeRequestedRoutedEventArgs methodInvokeRequestedEventArgs =
                new MethodInvokeRequestedRoutedEventArgs(MethodPresenter.MethodInvokeRequestedEvent)
            {
                MethodInformation = mi,
                Parameters = parameters
            };
            RaiseEvent(methodInvokeRequestedEventArgs);

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

                try
                {
                    object retVal = mi.Invoke(obj, parameters);
                    methodReturnValue = mi.ReturnType != typeof(void) ? new OutputValueViewModel(retVal) : null;
                }
                catch (ThreadAbortException tae)
                {
                    isAborted = true;
                }
                catch (System.Reflection.TargetInvocationException tie)
                {
                    methodException = new OutputValueViewModel(tie.InnerException);
                }
                catch (Exception ex)
                {
                    methodException = new OutputValueViewModel(ex);
                }
                finally
                {
                    if (!isAborted)
                    {
                        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;                              

                                IsInvoking = false;

                                // Raise MethodInvoked event.
                                MethodInvokedRoutedEventArgs methodInvokedEventArgs =
                                    new MethodInvokedRoutedEventArgs(MethodPresenter.MethodInvokedEvent)
                                    {
                                        MethodInformation = mi,
                                        MethodResult = methodResult
                                    };
                                RaiseEvent(methodInvokedEventArgs);

                            }));
                    }
                }

                _invokeMethodThread = null;
            });

            _invokeMethodThread.Start();
        }

        private void OnMethodResultRemoved(MethodResultViewModel result)
        {
            if (CurrentMethodResult == result)
            {
                CurrentMethodResult = null;
            }

            MethodResults.Remove(result);

            if (MethodResults.Count < 1)
            {
                HasResults = false;
            }
        }

        private void OnMethodResultShown(MethodResultViewModel result)
        {
            // Raise MethodResultShown event.
            MethodResultShownRoutedEventArgs newEventArgs = new MethodResultShownRoutedEventArgs(MethodPresenter.MethodResultShownEvent)
            {
                MethodResult = result
            };
            RaiseEvent(newEventArgs);
        }

        private Thread _invokeMethodThread;
        #endregion

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

        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;
        }
        #endregion

        #region ClearMethodResultsCommand
        private static RoutedCommand _clearMethodResultsCommand;
        public static RoutedCommand ClearMethodResultsCommand
        {
            get { return _clearMethodResultsCommand ?? (_clearMethodResultsCommand = new RoutedCommand("ClearMethodResults", typeof(MethodPresenter))); }
        }

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

            e.CanExecute = mp.HasResults;
        }

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

            mp.ClearMethodResults();
        }

        private void ClearMethodResults()
        {
            MethodResults.Clear();
            HasResults = false;
        }
        #endregion

        #endregion

        static MethodPresenter()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodPresenter), new FrameworkPropertyMetadata(typeof(MethodPresenter)));

            CommandBinding invokeMethodBinding = new CommandBinding(InvokeMethodCommand,
               ExecuteInvokeMethodCommand, CanExecuteInvokeMethodCommand);
            CommandManager.RegisterClassCommandBinding(typeof(MethodPresenter), invokeMethodBinding);

            CommandBinding stopInvokeMethodBinding = new CommandBinding(StopInvokeMethodCommand,
              ExecuteStopInvokeMethodCommand, CanExecuteStopInvokeMethodCommand);
            CommandManager.RegisterClassCommandBinding(typeof(MethodPresenter), stopInvokeMethodBinding);

            CommandBinding clearMethodResultsBinding = new CommandBinding(ClearMethodResultsCommand,
                ExecuteClearMethodResultsCommand, CanExecuteClearMethodResultsCommand);
            CommandManager.RegisterClassCommandBinding(typeof(MethodPresenter), clearMethodResultsBinding);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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
Follow on   LinkedIn

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