Server/FormEditObject.cs
author StephaneLenclud
Thu, 25 Aug 2016 13:12:54 +0200
changeset 254 181323a59047
parent 247 afdbe76ab03b
child 260 d44943088c67
permissions -rw-r--r--
Published v1.0.1.0.
Updating Harmony library to v0.5.0
     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                 iComboBoxObjectType.Items.Add(item);
    46             }
    47 
    48             if (Object == null)
    49             {
    50                 // Creating new issue, select our first item
    51                 iComboBoxObjectType.SelectedIndex = 0;
    52             }
    53             else
    54             {
    55                 // Editing existing object
    56                 // Look up our item in our object type combobox
    57                 foreach (ItemObjectType item in iComboBoxObjectType.Items)
    58                 {
    59                     if (item.Type == Object.GetType())
    60                     {
    61                         iComboBoxObjectType.SelectedItem = item;
    62                     }
    63                 }
    64 
    65             }
    66         }
    67 
    68         /// <summary>
    69         /// 
    70         /// </summary>
    71         /// <param name="sender"></param>
    72         /// <param name="e"></param>
    73         private void buttonOk_Click(object sender, EventArgs e)
    74         {
    75             FetchPropertiesValue(Object);
    76             if (!Object.IsValid())
    77             {
    78                 // Tell closing event to cancel
    79                 DialogResult = DialogResult.None;
    80             }
    81         }
    82 
    83 
    84         /// <summary>
    85         /// 
    86         /// </summary>
    87         /// <param name="sender"></param>
    88         /// <param name="e"></param>
    89         private void FormEditObject_FormClosing(object sender, FormClosingEventArgs e)
    90         {
    91             //Check if we need to cancel the closing of our form.
    92             e.Cancel = DialogResult == DialogResult.None;
    93 
    94             if (!e.Cancel)
    95             {
    96                 //Exit edit mode
    97                 Object.CurrentState = SharpLib.Ear.Object.State.Rest;
    98                 Object.PropertyChanged -= PropertyChangedEventHandlerThreadSafe;
    99             }
   100         }
   101 
   102         /// <summary>
   103         /// 
   104         /// </summary>
   105         /// <param name="sender"></param>
   106         /// <param name="e"></param>
   107         private void comboBoxActionType_SelectedIndexChanged(object sender, EventArgs e)
   108         {
   109             //Instantiate an action corresponding to our type
   110             Type actionType = ((ItemObjectType) iComboBoxObjectType.SelectedItem).Type;
   111             //Create another type of action only if needed
   112             if (Object == null || Object.GetType() != actionType)
   113             {
   114                 Object = (T)Activator.CreateInstance(actionType);
   115             }
   116 
   117             //Create input fields
   118             UpdateControls();
   119         }
   120 
   121 
   122         /// <summary>
   123         /// Get properties values from our generated input fields
   124         /// </summary>
   125         private void FetchPropertiesValue(T aObject)
   126         {
   127             int ctrlIndex = 0;
   128             //For each of our properties
   129             foreach (PropertyInfo pi in aObject.GetType().GetProperties())
   130             {
   131                 //Get our property attribute
   132                 AttributeObjectProperty[] attributes = ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
   133                 if (attributes.Length != 1)
   134                 {
   135                     //No attribute, skip this property then.
   136                     continue;
   137                 }
   138                 AttributeObjectProperty attribute = attributes[0];
   139 
   140                 //Check that we support this type of property
   141                 if (!IsPropertyTypeSupported(pi))
   142                 {
   143                     continue;
   144                 }
   145 
   146                 //Now fetch our property value
   147                 GetPropertyValueFromControl(iTableLayoutPanel.Controls[ctrlIndex+1], pi, aObject); //+1 otherwise we get the label
   148 
   149                 ctrlIndex+=2; //Jump over the label too
   150             }
   151         }
   152 
   153         /// <summary>
   154         /// Extend this function to support reading new types of properties.
   155         /// </summary>
   156         /// <param name="aObject"></param>
   157         private void GetPropertyValueFromControl(Control aControl, PropertyInfo aInfo, T aObject)
   158         {
   159             if (aInfo.PropertyType == typeof(int))
   160             {
   161                 NumericUpDown ctrl=(NumericUpDown)aControl;
   162                 aInfo.SetValue(aObject,(int)ctrl.Value);
   163             }
   164             else if (aInfo.PropertyType.IsEnum)
   165             {
   166                 // Instantiate our enum
   167                 object enumValue= Activator.CreateInstance(aInfo.PropertyType);
   168                 // Parse our enum from combo box
   169                 enumValue = Enum.Parse(aInfo.PropertyType,((ComboBox)aControl).SelectedItem.ToString());
   170                 //enumValue = ((ComboBox)aControl).SelectedValue;
   171                 // Set enum value
   172                 aInfo.SetValue(aObject, enumValue);
   173             }
   174             else if (aInfo.PropertyType == typeof(bool))
   175             {
   176                 CheckBox ctrl = (CheckBox)aControl;
   177                 aInfo.SetValue(aObject, ctrl.Checked);
   178             }
   179             else if (aInfo.PropertyType == typeof(string))
   180             {
   181                 TextBox ctrl = (TextBox)aControl;
   182                 aInfo.SetValue(aObject, ctrl.Text);
   183             }
   184             else if (aInfo.PropertyType == typeof(PropertyFile))
   185             {
   186                 Button ctrl = (Button)aControl;
   187                 PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
   188                 aInfo.SetValue(aObject, value);
   189             }
   190             else if (aInfo.PropertyType == typeof(PropertyComboBox))
   191             {
   192                 ComboBox ctrl = (ComboBox)aControl;
   193                 string currentItem = ctrl.SelectedItem.ToString();
   194                 PropertyComboBox value = (PropertyComboBox)aInfo.GetValue(aObject);
   195                 value.CurrentItem = currentItem;
   196                 //Not strictly needed but makes sure the set method is called
   197                 aInfo.SetValue(aObject, value);                
   198             }
   199             else if (aInfo.PropertyType == typeof(PropertyButton))
   200             {
   201                 Button ctrl = (Button)aControl;
   202                 PropertyButton value = new PropertyButton { Text = ctrl.Text };
   203                 aInfo.SetValue(aObject, value);
   204             }
   205 
   206             //TODO: add support for other types here
   207         }
   208 
   209 
   210         /// <summary>
   211         /// Create a control for the given property.
   212         /// </summary>
   213         /// <param name="aInfo"></param>
   214         /// <param name="aAttribute"></param>
   215         /// <param name="aObject"></param>
   216         /// <returns></returns>
   217         private Control CreateControlForProperty(PropertyInfo aInfo, AttributeObjectProperty aAttribute, T aObject)
   218         {
   219             if (aInfo.PropertyType == typeof(int))
   220             {
   221                 //Integer properties are using numeric editor
   222                 NumericUpDown ctrl = new NumericUpDown();
   223                 ctrl.AutoSize = true;
   224                 ctrl.Minimum = Int32.Parse(aAttribute.Minimum);
   225                 ctrl.Maximum = Int32.Parse(aAttribute.Maximum);
   226                 ctrl.Increment = Int32.Parse(aAttribute.Increment);
   227                 ctrl.Value = (int)aInfo.GetValue(aObject);
   228                 // Hook-in change notification after setting the value 
   229                 ctrl.ValueChanged += ControlValueChanged;
   230                 return ctrl;
   231             }
   232             else if (aInfo.PropertyType.IsEnum)
   233             {
   234                 //Enum properties are using combo box
   235                 ComboBox ctrl = new ComboBox();
   236                 ctrl.AutoSize = true;
   237                 ctrl.Sorted = true;
   238                 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
   239                 //Data source is fine but it gives us duplicate entries for duplicated enum values
   240                 //ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);
   241 
   242                 //Therefore we need to explicitly create our items
   243                 Size cbSize = new Size(0, 0);
   244                 foreach (string name in aInfo.PropertyType.GetEnumNames())
   245                 {
   246                     ctrl.Items.Add(name.ToString());
   247                     Graphics g = this.CreateGraphics();
   248                     //Since combobox autosize would not work we need to get measure text ourselves
   249                     SizeF size = g.MeasureString(name.ToString(), ctrl.Font);
   250                     cbSize.Width = Math.Max(cbSize.Width, (int)size.Width);
   251                     cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
   252                 }
   253 
   254                 //Make sure our combobox is large enough
   255                 ctrl.MinimumSize = cbSize;
   256 
   257                 // Instantiate our enum
   258                 object enumValue = Activator.CreateInstance(aInfo.PropertyType);
   259                 enumValue = aInfo.GetValue(aObject);
   260                 //Set the current item
   261                 ctrl.SelectedItem = enumValue.ToString();
   262                 // Hook-in change notification after setting the value 
   263                 ctrl.SelectedIndexChanged += ControlValueChanged;
   264 
   265                 return ctrl;
   266             }
   267             else if (aInfo.PropertyType == typeof(bool))
   268             {
   269                 CheckBox ctrl = new CheckBox();
   270                 ctrl.AutoSize = true;
   271                 ctrl.Text = aAttribute.Description;
   272                 ctrl.Checked = (bool)aInfo.GetValue(aObject);
   273                 // Hook-in change notification after setting the value 
   274                 ctrl.CheckedChanged += ControlValueChanged;
   275                 return ctrl;
   276             }
   277             else if (aInfo.PropertyType == typeof(string))
   278             {
   279                 TextBox ctrl = new TextBox();
   280                 ctrl.AutoSize = true;
   281                 ctrl.Text = (string)aInfo.GetValue(aObject);
   282                 // Hook-in change notification after setting the value 
   283                 ctrl.TextChanged += ControlValueChanged;
   284                 return ctrl;
   285             }
   286             else if (aInfo.PropertyType == typeof(PropertyFile))
   287             {
   288                 // We have a file property
   289                 // Create a button that will trigger the open file dialog to select our file.
   290                 Button ctrl = new Button();
   291                 ctrl.AutoSize = true;
   292                 ctrl.Text = ((PropertyFile)aInfo.GetValue(aObject)).FullPath;
   293                 // Add lambda expression to Click event
   294                 ctrl.Click += (sender, e) =>
   295                 {
   296                     // Create open file dialog
   297                     OpenFileDialog ofd = new OpenFileDialog();
   298                     ofd.RestoreDirectory = true;
   299                     // Use file filter specified by our property
   300                     ofd.Filter = aAttribute.Filter;
   301                     // Show our dialog
   302                     if (DlgBox.ShowDialog(ofd) == DialogResult.OK)
   303                     {
   304                         // Fetch selected file name
   305                         ctrl.Text = ofd.FileName;
   306                     }
   307                 };
   308 
   309                 // Hook-in change notification after setting the value 
   310                 ctrl.TextChanged += ControlValueChanged;
   311                 return ctrl;
   312             }
   313             else if (aInfo.PropertyType == typeof(PropertyComboBox))
   314             {
   315                 //ComboBox property
   316                 ComboBox ctrl = new ComboBox();
   317                 ctrl.AutoSize = true;
   318                 ctrl.Sorted = true;
   319                 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
   320                 //Data source is such a pain to set the current item
   321                 //ctrl.DataSource = ((PropertyComboBox)aInfo.GetValue(aObject)).Items;                
   322 
   323                 PropertyComboBox pcb = ((PropertyComboBox)aInfo.GetValue(aObject));
   324                 foreach (string item in pcb.Items)
   325                 {
   326                     ctrl.Items.Add(item);
   327                 }
   328 
   329                 ctrl.SelectedItem = ((PropertyComboBox)aInfo.GetValue(aObject)).CurrentItem;
   330                 //
   331                 return ctrl;
   332             }
   333             else if (aInfo.PropertyType == typeof(PropertyButton))
   334             {
   335                 // We have a button property
   336                 // Create a button that will trigger the custom action.
   337                 Button ctrl = new Button();
   338                 ctrl.AutoSize = true;
   339                 ctrl.Text = ((PropertyButton)aInfo.GetValue(aObject)).Text;
   340                 // Hook in click event
   341                 ctrl.Click += ((PropertyButton)aInfo.GetValue(aObject)).ClickEventHandler;
   342                 // Hook-in change notification after setting the value 
   343                 ctrl.TextChanged += ControlValueChanged;
   344                 return ctrl;
   345             }
   346 
   347             //TODO: add support for other control type here
   348             return null;
   349         }
   350 
   351         /// <summary>
   352         /// Don't forget to extend that one and adding types
   353         /// </summary>
   354         /// <returns></returns>
   355         private bool IsPropertyTypeSupported(PropertyInfo aInfo)
   356         {
   357             if (aInfo.PropertyType == typeof(int))
   358             {
   359                 return true;
   360             }
   361             else if (aInfo.PropertyType.IsEnum)
   362             {
   363                 return true;
   364             }
   365             else if (aInfo.PropertyType == typeof(bool))
   366             {
   367                 return true;
   368             }
   369             else if (aInfo.PropertyType == typeof(string))
   370             {
   371                 return true;
   372             }
   373             else if (aInfo.PropertyType == typeof(PropertyFile))
   374             {
   375                 return true;
   376             }
   377             else if (aInfo.PropertyType == typeof(PropertyComboBox))
   378             {
   379                 return true;
   380             }
   381             else if (aInfo.PropertyType == typeof(PropertyButton))
   382             {
   383                 return true;
   384             }
   385 
   386             //TODO: add support for other type here
   387 
   388             return false;
   389         }
   390 
   391         /// <summary>
   392         /// Update our table layout.
   393         /// Will instantiated every field control as defined by our object.
   394         /// </summary>
   395         /// <param name="aLayout"></param>
   396         private void UpdateControls()
   397         {
   398 
   399             toolTip.RemoveAll();
   400             //Debug.Print("UpdateTableLayoutPanel")
   401             //First clean our current panel
   402             iTableLayoutPanel.Controls.Clear();
   403             iTableLayoutPanel.RowStyles.Clear();
   404             iTableLayoutPanel.ColumnStyles.Clear();
   405             iTableLayoutPanel.RowCount = 0;
   406 
   407             //We always want two columns: one for label and one for the field
   408             iTableLayoutPanel.ColumnCount = 2;
   409             iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
   410             iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
   411 
   412 
   413             if (Object == null)
   414             {
   415                 //Just drop it
   416                 return;
   417             }
   418 
   419             UpdateStaticControls();
   420 
   421             //IEnumerable<PropertyInfo> properties = aObject.GetType().GetProperties().Where(
   422             //    prop => Attribute.IsDefined(prop, typeof(AttributeObjectProperty)));
   423 
   424             //TODO: Do this whenever a field changes
   425             iLabelBrief.Text = Object.Brief();
   426 
   427 
   428             foreach (PropertyInfo pi in Object.GetType().GetProperties())
   429             {
   430                 AttributeObjectProperty[] attributes = ((AttributeObjectProperty[])pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
   431                 if (attributes.Length != 1)
   432                 {
   433                     continue;
   434                 }
   435 
   436                 AttributeObjectProperty attribute = attributes[0];
   437 
   438                 //Before anything we need to check if that kind of property is supported by our UI
   439                 //Create the editor
   440                 Control ctrl = CreateControlForProperty(pi, attribute, Object);
   441                 if (ctrl == null)
   442                 {
   443                     //Property type not supported
   444                     continue;
   445                 }
   446 
   447                 //Add a new row
   448                 iTableLayoutPanel.RowCount++;
   449                 iTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
   450                 //Create the label
   451                 Label label = new Label();
   452                 label.AutoSize = true;
   453                 label.Dock = DockStyle.Fill;
   454                 label.TextAlign = ContentAlignment.MiddleCenter;
   455                 label.Text = attribute.Name;
   456                 toolTip.SetToolTip(label, attribute.Description);
   457                 iTableLayoutPanel.Controls.Add(label, 0, iTableLayoutPanel.RowCount-1);
   458 
   459                 //Add our editor to our form
   460                 iTableLayoutPanel.Controls.Add(ctrl, 1, iTableLayoutPanel.RowCount - 1);
   461                 //Add tooltip to editor too
   462                 toolTip.SetToolTip(ctrl, attribute.Description);
   463 
   464             }
   465 
   466             //Entrer object edit mode
   467             Object.CurrentState = SharpLib.Ear.Object.State.Edit;
   468             Object.PropertyChanged += PropertyChangedEventHandlerThreadSafe;
   469         }
   470 
   471         /// <summary>
   472         /// 
   473         /// </summary>
   474         /// <param name="sender"></param>
   475         /// <param name="e"></param>
   476         void PropertyChangedEventHandlerThreadSafe(object sender, PropertyChangedEventArgs e)
   477         {
   478             if (this.InvokeRequired)
   479             {
   480                 //Not in the proper thread, invoke ourselves
   481                 PropertyChangedEventHandler d = new PropertyChangedEventHandler(PropertyChangedEventHandlerThreadSafe);
   482                 this.Invoke(d, new object[] { sender, e });
   483             }
   484             else
   485             {
   486                 // We could test the name of the property that has changed as follow
   487                 // It's currently not needed though
   488                 //if (e.PropertyName == "Brief")
   489 
   490                 // Our object has changed behind our back.
   491                 // That's currently only the case for HID events that are listening for inputs.
   492                 if (Object is EventHid)
   493                 {
   494                     //HID can't do full control updates for some reason
   495                     //We are getting spammed with HID events after a few clicks
   496                     //We need to investigate, HID bug?
   497                     UpdateStaticControls();
   498                 }
   499                 else
   500                 {
   501                     UpdateControls();
   502                 }
   503             }
   504         }
   505 
   506         private void buttonTest_Click(object sender, EventArgs e)
   507         {
   508             FetchPropertiesValue(Object);
   509 
   510             //If our object has a test method with no parameters just run it then
   511             MethodInfo info = Object.GetType().GetMethod("Test");
   512             if ( info != null && info.GetParameters().Length==0)
   513             {
   514                 info.Invoke(Object,null);
   515             }
   516 
   517         }
   518 
   519 
   520         /// <summary>
   521         /// 
   522         /// </summary>
   523         /// <param name="sender"></param>
   524         /// <param name="e"></param>
   525         private void ControlValueChanged(object sender, EventArgs e)
   526         {
   527             UpdateObject();
   528         }
   529 
   530         /// <summary>
   531         /// 
   532         /// </summary>
   533         private void UpdateObject()
   534         {
   535             // Update our object with the content of our controls
   536             FetchPropertiesValue(Object);
   537 
   538             UpdateStaticControls();
   539             //
   540             //PerformLayout();
   541         }
   542 
   543         /// <summary>
   544         /// 
   545         /// </summary>
   546         private void UpdateStaticControls()
   547         {
   548             // Update OK and test button status
   549             iButtonOk.Enabled = Object.IsValid();
   550             iButtonTest.Enabled = iButtonOk.Enabled;
   551 
   552             // Update brief title
   553             iLabelBrief.Text = Object.Brief();
   554 
   555             // Update object description
   556             iLabelDescription.Text = Object.Description;
   557         }
   558 
   559         /// <summary>
   560         /// 
   561         /// </summary>
   562         /// <param name="sender"></param>
   563         /// <param name="e"></param>
   564         private void iComboBoxObjectType_KeyPress(object sender, KeyPressEventArgs e)
   565         {
   566             //Special case for HID events
   567             if (Object is EventHid)
   568             {
   569                 //Disable handling of key input as we are using key input for changing our event
   570                 e.Handled = true;
   571             }
   572         }
   573     }
   574 }