Server/FormEditObject.cs
author StephaneLenclud
Thu, 18 Aug 2016 17:13:21 +0200
changeset 239 dd7770b97916
parent 238 c92587ddabcd
child 243 cc2251d065db
permissions -rw-r--r--
Improved object editor dialog validation.
Added EAR support for object validation.
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Data;
     5 using System.Diagnostics;
     6 using System.Drawing;
     7 using System.Linq;
     8 using System.Text;
     9 using System.Threading.Tasks;
    10 using System.Windows.Forms;
    11 using SharpLib.Display;
    12 using SharpLib.Ear;
    13 using System.Reflection;
    14 using Microsoft.VisualBasic.CompilerServices;
    15 using SharpLib.Utils;
    16 using CodeProject.Dialog;
    17 using System.IO;
    18 
    19 namespace SharpDisplayManager
    20 {
    21     /// <summary>
    22     /// Object edit dialog form.
    23     /// </summary>
    24     public partial class FormEditObject<T> : Form where T: SharpLib.Ear.Object
    25     {
    26         public T Object = null;
    27 
    28         public FormEditObject()
    29         {
    30             InitializeComponent();
    31         }
    32 
    33         /// <summary>
    34         /// 
    35         /// </summary>
    36         /// <param name="sender"></param>
    37         /// <param name="e"></param>
    38         private void FormEditAction_Load(object sender, EventArgs e)
    39         {
    40             // Populate registered object types
    41             IEnumerable < Type > types = Reflection.GetConcreteClassesDerivedFrom<T>();
    42             foreach (Type type in types)
    43             {
    44                 ItemObjectType item = new ItemObjectType(type);
    45                 comboBoxActionType.Items.Add(item);
    46             }
    47 
    48             if (Object == null)
    49             {
    50                 // Creating new issue, select our first item
    51                 comboBoxActionType.SelectedIndex = 0;
    52             }
    53             else
    54             {
    55                 // Editing existing object
    56                 // Look up our item in our combobox
    57                 foreach (ItemObjectType item in comboBoxActionType.Items)
    58                 {
    59                     if (item.Type == Object.GetType())
    60                     {
    61                         comboBoxActionType.SelectedItem = item;
    62                     }
    63                 }
    64             }
    65         }
    66 
    67         private void buttonOk_Click(object sender, EventArgs e)
    68         {
    69             FetchPropertiesValue(Object);
    70             if (!Object.IsValid())
    71             {
    72                 // Tell for closing event to abort
    73                 DialogResult = DialogResult.None;
    74             }
    75         }
    76 
    77 
    78         private void FormEditObject_FormClosing(object sender, FormClosingEventArgs e)
    79         {
    80             e.Cancel = DialogResult == DialogResult.None;
    81         }
    82 
    83         private void comboBoxActionType_SelectedIndexChanged(object sender, EventArgs e)
    84         {
    85             //Instantiate an action corresponding to our type
    86             Type actionType = ((ItemObjectType) comboBoxActionType.SelectedItem).Type;
    87             //Create another type of action only if needed
    88             if (Object == null || Object.GetType() != actionType)
    89             {
    90                 Object = (T)Activator.CreateInstance(actionType);
    91             }
    92 
    93             //Disable ok button if our object is not valid
    94             buttonOk.Enabled = Object.IsValid();
    95 
    96             //Create input fields
    97             UpdateTableLayoutPanel(Object);
    98         }
    99 
   100 
   101         /// <summary>
   102         /// Get properties values from our generated input fields
   103         /// </summary>
   104         private void FetchPropertiesValue(T aObject)
   105         {
   106             int ctrlIndex = 0;
   107             foreach (PropertyInfo pi in aObject.GetType().GetProperties())
   108             {
   109                 AttributeObjectProperty[] attributes =
   110                     ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
   111                 if (attributes.Length != 1)
   112                 {
   113                     continue;
   114                 }
   115 
   116                 AttributeObjectProperty attribute = attributes[0];
   117 
   118                 if (!IsPropertyTypeSupported(pi))
   119                 {
   120                     continue;
   121                 }
   122 
   123                 GetPropertyValueFromControl(iTableLayoutPanel.Controls[ctrlIndex+1], pi, aObject); //+1 otherwise we get the label
   124 
   125                 ctrlIndex+=2; //Jump over the label too
   126             }
   127         }
   128 
   129         /// <summary>
   130         /// Extend this function to support reading new types of properties.
   131         /// </summary>
   132         /// <param name="aObject"></param>
   133         private void GetPropertyValueFromControl(Control aControl, PropertyInfo aInfo, T aObject)
   134         {
   135             if (aInfo.PropertyType == typeof(int))
   136             {
   137                 NumericUpDown ctrl=(NumericUpDown)aControl;
   138                 aInfo.SetValue(aObject,(int)ctrl.Value);
   139             }
   140             else if (aInfo.PropertyType.IsEnum)
   141             {
   142                 // Instantiate our enum
   143                 object enumValue= Activator.CreateInstance(aInfo.PropertyType);
   144                 // Parse our enum from combo box
   145                 enumValue = Enum.Parse(aInfo.PropertyType,((ComboBox)aControl).SelectedItem.ToString());
   146                 //enumValue = ((ComboBox)aControl).SelectedValue;
   147                 // Set enum value
   148                 aInfo.SetValue(aObject, enumValue);
   149             }
   150             else if (aInfo.PropertyType == typeof(bool))
   151             {
   152                 CheckBox ctrl = (CheckBox)aControl;
   153                 aInfo.SetValue(aObject, ctrl.Checked);
   154             }
   155             else if (aInfo.PropertyType == typeof(string))
   156             {
   157                 TextBox ctrl = (TextBox)aControl;
   158                 aInfo.SetValue(aObject, ctrl.Text);
   159             }
   160             else if (aInfo.PropertyType == typeof(PropertyFile))
   161             {
   162                 Button ctrl = (Button)aControl;
   163                 PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
   164                 aInfo.SetValue(aObject, value);
   165             }
   166             //TODO: add support for other types here
   167         }
   168 
   169 
   170         /// <summary>
   171         /// Create a control for the given property.
   172         /// </summary>
   173         /// <param name="aInfo"></param>
   174         /// <param name="aAttribute"></param>
   175         /// <param name="aObject"></param>
   176         /// <returns></returns>
   177         private Control CreateControlForProperty(PropertyInfo aInfo, AttributeObjectProperty aAttribute, T aObject)
   178         {
   179             if (aInfo.PropertyType == typeof(int))
   180             {
   181                 //Integer properties are using numeric editor
   182                 NumericUpDown ctrl = new NumericUpDown();
   183                 ctrl.AutoSize = true;
   184                 ctrl.Minimum = Int32.Parse(aAttribute.Minimum);
   185                 ctrl.Maximum = Int32.Parse(aAttribute.Maximum);
   186                 ctrl.Increment = Int32.Parse(aAttribute.Increment);
   187                 ctrl.Value = (int)aInfo.GetValue(aObject);
   188                 return ctrl;
   189             }
   190             else if (aInfo.PropertyType.IsEnum)
   191             {
   192                 //Enum properties are using combo box
   193                 ComboBox ctrl = new ComboBox();
   194                 ctrl.AutoSize = true;                
   195                 ctrl.Sorted = true;                
   196                 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
   197                 //Data source is fine but it gives us duplicate entries for duplicated enum values
   198                 //ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);
   199 
   200                 //Therefore we need to explicitly create our items
   201                 Size cbSize = new Size(0,0);
   202                 foreach (string name in aInfo.PropertyType.GetEnumNames())
   203                 {
   204                     ctrl.Items.Add(name.ToString());
   205                     Graphics g = this.CreateGraphics();
   206                     //Since combobox autosize would not work we need to get measure text ourselves
   207                     SizeF size=g.MeasureString(name.ToString(), ctrl.Font);
   208                     cbSize.Width = Math.Max(cbSize.Width,(int)size.Width);
   209                     cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
   210                 }
   211 
   212                 //Make sure our combobox is large enough
   213                 ctrl.MinimumSize = cbSize;
   214 
   215                 // Instantiate our enum
   216                 object enumValue = Activator.CreateInstance(aInfo.PropertyType);
   217                 enumValue = aInfo.GetValue(aObject);
   218                 //Set the current item
   219                 ctrl.SelectedItem = enumValue.ToString();
   220 
   221                 return ctrl;
   222             }
   223             else if (aInfo.PropertyType == typeof(bool))
   224             {
   225                 CheckBox ctrl = new CheckBox();
   226                 ctrl.AutoSize = true;
   227                 ctrl.Text = aAttribute.Description;
   228                 ctrl.Checked = (bool)aInfo.GetValue(aObject);                
   229                 return ctrl;
   230             }
   231             else if (aInfo.PropertyType == typeof(string))
   232             {
   233                 TextBox ctrl = new TextBox();
   234                 ctrl.AutoSize = true;
   235                 ctrl.Text = (string)aInfo.GetValue(aObject);
   236                 return ctrl;
   237             }
   238             else if (aInfo.PropertyType == typeof(PropertyFile))
   239             {
   240                 // We have a file property
   241                 // Create a button that will trigger the open file dialog to select our file.
   242                 Button ctrl = new Button();
   243                 ctrl.AutoSize = true;
   244                 ctrl.Text = ((PropertyFile)aInfo.GetValue(aObject)).FullPath;
   245 
   246                 // Add lambda expression to Click event
   247                 ctrl.Click += (sender, e) =>
   248                 {
   249                     // Create open file dialog
   250                     OpenFileDialog ofd = new OpenFileDialog();
   251                     ofd.RestoreDirectory = true;
   252                     // Use file filter specified by our property
   253                     ofd.Filter = aAttribute.Filter;
   254                     // Show our dialog
   255                     if (DlgBox.ShowDialog(ofd) == DialogResult.OK)
   256                     {
   257                         // Fetch selected file name
   258                         ctrl.Text = ofd.FileName;
   259                         //Enable Ok button then
   260                         buttonOk.Enabled = Object.IsValid();
   261                     }
   262                 };
   263 
   264                 return ctrl;
   265             }
   266             //TODO: add support for other control type here
   267 
   268             return null;
   269         }
   270 
   271         /// <summary>
   272         /// Don't forget to extend that one and adding types
   273         /// </summary>
   274         /// <returns></returns>
   275         private bool IsPropertyTypeSupported(PropertyInfo aInfo)
   276         {
   277             if (aInfo.PropertyType == typeof(int))
   278             {
   279                 return true;
   280             }
   281             else if (aInfo.PropertyType.IsEnum)
   282             {
   283                 return true;
   284             }
   285             else if (aInfo.PropertyType == typeof(bool))
   286             {
   287                 return true;
   288             }
   289             else if (aInfo.PropertyType == typeof(string))
   290             {
   291                 return true;
   292             }
   293             else if (aInfo.PropertyType == typeof(PropertyFile))
   294             {
   295                 return true;
   296             }
   297             //TODO: add support for other type here
   298 
   299             return false;
   300         }
   301 
   302         /// <summary>
   303         /// Update our table layout.
   304         /// Will instantiated every field control as defined by our action.
   305         /// Fields must be specified by rows from the left.
   306         /// </summary>
   307         /// <param name="aLayout"></param>
   308         private void UpdateTableLayoutPanel(T aObject)
   309         {
   310             toolTip.RemoveAll();
   311             //Debug.Print("UpdateTableLayoutPanel")
   312             //First clean our current panel
   313             iTableLayoutPanel.Controls.Clear();
   314             iTableLayoutPanel.RowStyles.Clear();
   315             iTableLayoutPanel.ColumnStyles.Clear();
   316             iTableLayoutPanel.RowCount = 0;
   317 
   318             //We always want two columns: one for label and one for the field
   319             iTableLayoutPanel.ColumnCount = 2;
   320             iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
   321             iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
   322 
   323 
   324             if (aObject == null)
   325             {
   326                 //Just drop it
   327                 return;
   328             }
   329             
   330             //IEnumerable<PropertyInfo> properties = aObject.GetType().GetProperties().Where(
   331             //    prop => Attribute.IsDefined(prop, typeof(AttributeObjectProperty)));
   332 
   333 
   334             foreach (PropertyInfo pi in aObject.GetType().GetProperties())
   335             {
   336                 AttributeObjectProperty[] attributes = ((AttributeObjectProperty[])pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
   337                 if (attributes.Length != 1)
   338                 {
   339                     continue;
   340                 }
   341 
   342                 AttributeObjectProperty attribute = attributes[0];
   343 
   344                 //Before anything we need to check if that kind of property is supported by our UI
   345                 //Create the editor
   346                 Control ctrl = CreateControlForProperty(pi, attribute, aObject);
   347                 if (ctrl == null)
   348                 {
   349                     //Property type not supported
   350                     continue;
   351                 }
   352 
   353                 //Add a new row
   354                 iTableLayoutPanel.RowCount++;
   355                 iTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
   356                 //Create the label
   357                 Label label = new Label();
   358                 label.AutoSize = true;
   359                 label.Dock = DockStyle.Fill;
   360                 label.TextAlign = ContentAlignment.MiddleCenter;
   361                 label.Text = attribute.Name;
   362                 toolTip.SetToolTip(label, attribute.Description);
   363                 iTableLayoutPanel.Controls.Add(label, 0, iTableLayoutPanel.RowCount-1);
   364 
   365                 //Add our editor to our form
   366                 iTableLayoutPanel.Controls.Add(ctrl, 1, iTableLayoutPanel.RowCount - 1);
   367                 //Add tooltip to editor too
   368                 toolTip.SetToolTip(ctrl, attribute.Description);
   369 
   370             }        
   371 
   372         }
   373 
   374         private void buttonTest_Click(object sender, EventArgs e)
   375         {
   376             FetchPropertiesValue(Object);
   377 
   378             //If our object has a test method with no parameters just run it then
   379             MethodInfo info = Object.GetType().GetMethod("Test");
   380             if ( info != null && info.GetParameters().Length==0)
   381             {
   382                 info.Invoke(Object,null);
   383             }
   384 
   385         }
   386     }
   387 }