Click here to Skip to main content
15,891,204 members
Articles / Desktop Programming / WPF

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

Rate me:
Please Sign up or sign in to vote.
4.94/5 (27 votes)
5 Jan 2012CPOL11 min read 37.4K   924   49  
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.
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)


Written By
Software Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions