Switch from NAudio to CSCore.
2 using System.Collections.Generic;
3 using System.ComponentModel;
5 using System.Diagnostics;
9 using System.Threading.Tasks;
10 using System.Windows.Forms;
11 using SharpLib.Display;
13 using System.Reflection;
14 using Microsoft.VisualBasic.CompilerServices;
16 using CodeProject.Dialog;
19 namespace SharpDisplayManager
22 /// Object edit dialog form.
24 public partial class FormEditObject<T> : Form where T: SharpLib.Ear.Object
26 public T Object = null;
28 public FormEditObject()
30 InitializeComponent();
36 /// <param name="sender"></param>
37 /// <param name="e"></param>
38 private void FormEditAction_Load(object sender, EventArgs e)
40 // Populate registered object types
41 IEnumerable < Type > types = Reflection.GetConcreteClassesDerivedFrom<T>();
42 foreach (Type type in types)
44 ItemObjectType item = new ItemObjectType(type);
45 iComboBoxObjectType.Items.Add(item);
50 // Creating new issue, select our first item
51 iComboBoxObjectType.SelectedIndex = 0;
55 // Editing existing object
56 // Look up our item in our object type combobox
57 foreach (ItemObjectType item in iComboBoxObjectType.Items)
59 if (item.Type == Object.GetType())
61 iComboBoxObjectType.SelectedItem = item;
71 /// <param name="sender"></param>
72 /// <param name="e"></param>
73 private void buttonOk_Click(object sender, EventArgs e)
75 FetchPropertiesValue(Object);
76 if (!Object.IsValid())
78 // Tell closing event to cancel
79 DialogResult = DialogResult.None;
87 /// <param name="sender"></param>
88 /// <param name="e"></param>
89 private void FormEditObject_FormClosing(object sender, FormClosingEventArgs e)
91 //Check if we need to cancel the closing of our form.
92 e.Cancel = DialogResult == DialogResult.None;
97 Object.CurrentState = SharpLib.Ear.Object.State.Rest;
98 Object.PropertyChanged -= PropertyChangedEventHandlerThreadSafe;
105 /// <param name="sender"></param>
106 /// <param name="e"></param>
107 private void comboBoxActionType_SelectedIndexChanged(object sender, EventArgs e)
109 //Instantiate an action corresponding to our type
110 Type objectType = ((ItemObjectType) iComboBoxObjectType.SelectedItem).Type;
111 //Create another type of action only if needed
112 if (Object == null || Object.GetType() != objectType)
117 // Make sure we exit edit mode and unhook from events
118 Object.CurrentState = SharpLib.Ear.Object.State.Rest;
119 Object.PropertyChanged -= PropertyChangedEventHandlerThreadSafe;
123 Object = (T)Activator.CreateInstance(objectType);
124 //Keep the name when changing the type
128 //Create input fields
134 /// Get properties values from our generated input fields
136 private void FetchPropertiesValue(T aObject)
139 //For each of our properties
140 foreach (PropertyInfo pi in aObject.GetType().GetProperties())
142 //Get our property attribute
143 AttributeObjectProperty[] attributes = ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
144 if (attributes.Length != 1)
146 //No attribute, skip this property then.
149 AttributeObjectProperty attribute = attributes[0];
151 //Check that we support this type of property
152 if (!IsPropertyTypeSupported(pi))
157 //Now fetch our property value
158 GetPropertyValueFromControl(iTableLayoutPanel.Controls[ctrlIndex+1], pi, aObject); //+1 otherwise we get the label
160 ctrlIndex+=2; //Jump over the label too
165 /// Extend this function to support reading new types of properties.
167 /// <param name="aObject"></param>
168 private void GetPropertyValueFromControl(Control aControl, PropertyInfo aInfo, T aObject)
170 if (aInfo.PropertyType == typeof(int))
172 NumericUpDown ctrl=(NumericUpDown)aControl;
173 aInfo.SetValue(aObject,(int)ctrl.Value);
175 else if (aInfo.PropertyType.IsEnum)
177 // Instantiate our enum
178 object enumValue= Activator.CreateInstance(aInfo.PropertyType);
179 // Parse our enum from combo box
180 enumValue = Enum.Parse(aInfo.PropertyType,((ComboBox)aControl).SelectedItem.ToString());
181 //enumValue = ((ComboBox)aControl).SelectedValue;
183 aInfo.SetValue(aObject, enumValue);
185 else if (aInfo.PropertyType == typeof(bool))
187 CheckBox ctrl = (CheckBox)aControl;
188 aInfo.SetValue(aObject, ctrl.Checked);
190 else if (aInfo.PropertyType == typeof(string))
192 TextBox ctrl = (TextBox)aControl;
193 aInfo.SetValue(aObject, ctrl.Text);
195 else if (aInfo.PropertyType == typeof(PropertyFile))
197 Button ctrl = (Button)aControl;
198 PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
199 aInfo.SetValue(aObject, value);
201 else if (aInfo.PropertyType == typeof(PropertyComboBox))
203 ComboBox ctrl = (ComboBox)aControl;
204 string currentItem = ctrl.SelectedItem.ToString();
205 PropertyComboBox value = (PropertyComboBox)aInfo.GetValue(aObject);
206 value.CurrentItem = currentItem;
207 //Not strictly needed but makes sure the set method is called
208 aInfo.SetValue(aObject, value);
210 else if (aInfo.PropertyType == typeof(PropertyButton))
212 Button ctrl = (Button)aControl;
213 PropertyButton value = new PropertyButton { Text = ctrl.Text };
214 aInfo.SetValue(aObject, value);
217 //TODO: add support for other types here
222 /// Create a control for the given property.
224 /// <param name="aInfo"></param>
225 /// <param name="aAttribute"></param>
226 /// <param name="aObject"></param>
227 /// <returns></returns>
228 private Control CreateControlForProperty(PropertyInfo aInfo, AttributeObjectProperty aAttribute, T aObject)
230 if (aInfo.PropertyType == typeof(int))
232 //Integer properties are using numeric editor
233 NumericUpDown ctrl = new NumericUpDown();
234 ctrl.AutoSize = true;
235 ctrl.Minimum = Int32.Parse(aAttribute.Minimum);
236 ctrl.Maximum = Int32.Parse(aAttribute.Maximum);
237 ctrl.Increment = Int32.Parse(aAttribute.Increment);
238 ctrl.Value = (int)aInfo.GetValue(aObject);
239 // Hook-in change notification after setting the value
240 ctrl.ValueChanged += ControlValueChanged;
243 else if (aInfo.PropertyType.IsEnum)
245 //Enum properties are using combo box
246 ComboBox ctrl = new ComboBox();
247 ctrl.AutoSize = true;
249 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
250 //Data source is fine but it gives us duplicate entries for duplicated enum values
251 //ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);
253 //Therefore we need to explicitly create our items
254 Size cbSize = new Size(0, 0);
255 foreach (string name in aInfo.PropertyType.GetEnumNames())
257 ctrl.Items.Add(name.ToString());
258 Graphics g = this.CreateGraphics();
259 //Since combobox autosize would not work we need to get measure text ourselves
260 SizeF size = g.MeasureString(name.ToString(), ctrl.Font);
261 cbSize.Width = Math.Max(cbSize.Width, (int)size.Width);
262 cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
265 //Make sure our combobox is large enough
266 ctrl.MinimumSize = cbSize;
268 // Instantiate our enum
269 object enumValue = Activator.CreateInstance(aInfo.PropertyType);
270 enumValue = aInfo.GetValue(aObject);
271 //Set the current item
272 ctrl.SelectedItem = enumValue.ToString();
273 // Hook-in change notification after setting the value
274 ctrl.SelectedIndexChanged += ControlValueChanged;
278 else if (aInfo.PropertyType == typeof(bool))
280 CheckBox ctrl = new CheckBox();
281 ctrl.AutoSize = true;
282 ctrl.Text = aAttribute.Description;
283 ctrl.Checked = (bool)aInfo.GetValue(aObject);
284 // Hook-in change notification after setting the value
285 ctrl.CheckedChanged += ControlValueChanged;
288 else if (aInfo.PropertyType == typeof(string))
290 TextBox ctrl = new TextBox();
291 ctrl.AutoSize = true;
292 ctrl.Text = (string)aInfo.GetValue(aObject);
293 // Hook-in change notification after setting the value
294 ctrl.TextChanged += ControlValueChanged;
297 else if (aInfo.PropertyType == typeof(PropertyFile))
299 // We have a file property
300 // Create a button that will trigger the open file dialog to select our file.
301 Button ctrl = new Button();
302 ctrl.AutoSize = true;
303 ctrl.Text = ((PropertyFile)aInfo.GetValue(aObject)).FullPath;
304 // Add lambda expression to Click event
305 ctrl.Click += (sender, e) =>
307 // Create open file dialog
308 OpenFileDialog ofd = new OpenFileDialog();
309 ofd.RestoreDirectory = true;
310 // Use file filter specified by our property
311 ofd.Filter = aAttribute.Filter;
313 if (DlgBox.ShowDialog(ofd) == DialogResult.OK)
315 // Fetch selected file name
316 ctrl.Text = ofd.FileName;
320 // Hook-in change notification after setting the value
321 ctrl.TextChanged += ControlValueChanged;
324 else if (aInfo.PropertyType == typeof(PropertyComboBox))
327 ComboBox ctrl = new ComboBox();
328 ctrl.AutoSize = true;
330 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
331 //Data source is such a pain to set the current item
332 //ctrl.DataSource = ((PropertyComboBox)aInfo.GetValue(aObject)).Items;
334 PropertyComboBox pcb = ((PropertyComboBox)aInfo.GetValue(aObject));
335 foreach (string item in pcb.Items)
337 ctrl.Items.Add(item);
340 ctrl.SelectedItem = ((PropertyComboBox)aInfo.GetValue(aObject)).CurrentItem;
344 else if (aInfo.PropertyType == typeof(PropertyButton))
346 // We have a button property
347 // Create a button that will trigger the custom action.
348 Button ctrl = new Button();
349 ctrl.AutoSize = true;
350 ctrl.Text = ((PropertyButton)aInfo.GetValue(aObject)).Text;
351 // Hook in click event
352 ctrl.Click += ((PropertyButton)aInfo.GetValue(aObject)).ClickEventHandler;
353 // Hook-in change notification after setting the value
354 ctrl.TextChanged += ControlValueChanged;
358 //TODO: add support for other control type here
363 /// Don't forget to extend that one and adding types
365 /// <returns></returns>
366 private bool IsPropertyTypeSupported(PropertyInfo aInfo)
368 if (aInfo.PropertyType == typeof(int))
372 else if (aInfo.PropertyType.IsEnum)
376 else if (aInfo.PropertyType == typeof(bool))
380 else if (aInfo.PropertyType == typeof(string))
384 else if (aInfo.PropertyType == typeof(PropertyFile))
388 else if (aInfo.PropertyType == typeof(PropertyComboBox))
392 else if (aInfo.PropertyType == typeof(PropertyButton))
397 //TODO: add support for other type here
403 /// Update our table layout.
404 /// Will instantiated every field control as defined by our object.
406 /// <param name="aLayout"></param>
407 private void UpdateControls()
411 //Debug.Print("UpdateTableLayoutPanel")
412 //First clean our current panel
413 iTableLayoutPanel.Controls.Clear();
414 iTableLayoutPanel.RowStyles.Clear();
415 iTableLayoutPanel.ColumnStyles.Clear();
416 iTableLayoutPanel.RowCount = 0;
418 //We always want two columns: one for label and one for the field
419 iTableLayoutPanel.ColumnCount = 2;
420 iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
421 iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
430 UpdateStaticControls();
432 //IEnumerable<PropertyInfo> properties = aObject.GetType().GetProperties().Where(
433 // prop => Attribute.IsDefined(prop, typeof(AttributeObjectProperty)));
435 //TODO: Do this whenever a field changes
436 iLabelBrief.Text = Object.Brief();
439 foreach (PropertyInfo pi in Object.GetType().GetProperties())
441 AttributeObjectProperty[] attributes = ((AttributeObjectProperty[])pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
442 if (attributes.Length != 1)
447 AttributeObjectProperty attribute = attributes[0];
449 //Before anything we need to check if that kind of property is supported by our UI
451 Control ctrl = CreateControlForProperty(pi, attribute, Object);
454 //Property type not supported
459 iTableLayoutPanel.RowCount++;
460 iTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
462 Label label = new Label();
463 label.AutoSize = true;
464 label.Dock = DockStyle.Fill;
465 label.TextAlign = ContentAlignment.MiddleCenter;
466 label.Text = attribute.Name;
467 toolTip.SetToolTip(label, attribute.Description);
468 iTableLayoutPanel.Controls.Add(label, 0, iTableLayoutPanel.RowCount-1);
470 //Add our editor to our form
471 iTableLayoutPanel.Controls.Add(ctrl, 1, iTableLayoutPanel.RowCount - 1);
472 //Add tooltip to editor too
473 toolTip.SetToolTip(ctrl, attribute.Description);
477 //Entrer object edit mode
478 Object.CurrentState = SharpLib.Ear.Object.State.Edit;
479 Object.PropertyChanged += PropertyChangedEventHandlerThreadSafe;
485 /// <param name="sender"></param>
486 /// <param name="e"></param>
487 void PropertyChangedEventHandlerThreadSafe(object sender, PropertyChangedEventArgs e)
489 if (this.InvokeRequired)
491 //Not in the proper thread, invoke ourselves
492 PropertyChangedEventHandler d = new PropertyChangedEventHandler(PropertyChangedEventHandlerThreadSafe);
493 this.Invoke(d, new object[] { sender, e });
497 // We could test the name of the property that has changed as follow
498 // It's currently not needed though
499 //if (e.PropertyName == "Brief")
501 // Our object has changed behind our back.
502 // That's currently only the case for HID events that are listening for inputs.
503 if (Object is EventHid)
505 //HID can't do full control updates for some reason
506 //We are getting spammed with HID events after a few clicks
507 //We need to investigate, HID bug?
508 UpdateStaticControls();
517 private void buttonTest_Click(object sender, EventArgs e)
519 FetchPropertiesValue(Object);
521 //If our object has a test method with no parameters just run it then
522 MethodInfo info = Object.GetType().GetMethod("Test");
523 if ( info != null && info.GetParameters().Length==0)
525 info.Invoke(Object,null);
534 /// <param name="sender"></param>
535 /// <param name="e"></param>
536 private void ControlValueChanged(object sender, EventArgs e)
544 private void UpdateObject()
546 // Update our object with the content of our controls
547 FetchPropertiesValue(Object);
549 UpdateStaticControls();
557 private void UpdateStaticControls()
559 // Update OK and test button status
560 iButtonOk.Enabled = Object.IsValid();
561 iButtonTest.Enabled = iButtonOk.Enabled;
563 // Update brief title
564 iLabelBrief.Text = Object.Brief();
566 // Update object description
567 iLabelDescription.Text = Object.AttributeDescription;
573 /// <param name="sender"></param>
574 /// <param name="e"></param>
575 private void iComboBoxObjectType_KeyPress(object sender, KeyPressEventArgs e)
577 //Special case for HID events
578 if (Object is EventHid)
580 //Disable handling of key input as we are using key input for changing our event
585 private void iComboBoxObjectType_Enter(object sender, EventArgs e)
587 //Only edit HID event when our type combo box has the focus
588 // TODO: That's an ugly workaround, fix that somehow. Maybe by only doing HID scan when a button property has the focus
589 if (Object is EventHid)
591 Object.CurrentState = SharpLib.Ear.Object.State.Edit;
595 private void iComboBoxObjectType_Leave(object sender, EventArgs e)
597 //Only edit HID event when our type combo box has the focus
598 // TODO: That's an ugly workaround, fix that somehow. Maybe by only doing HID scan when a button property has the focus
599 if (Object is EventHid)
601 Object.CurrentState = SharpLib.Ear.Object.State.Rest;