io commander
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using Inspectron.Settings.Attributes;
|
||||
@@ -36,7 +38,12 @@ public class DefaultControlFactory : IControlFactory
|
||||
};
|
||||
}
|
||||
|
||||
if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase))
|
||||
// Check for List<T> types
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
return CreateListControl(name, description, type, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return CreatePathControl(name, description, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
@@ -62,6 +69,63 @@ public class DefaultControlFactory : IControlFactory
|
||||
}
|
||||
}
|
||||
|
||||
private Control CreateListControl(string name, string description, Type listType, object initialValue,
|
||||
Action<object> valueChangedCallback, OptionsWindow optionsWindow, string settingDescription,
|
||||
Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var elementType = listType.GetGenericArguments()[0];
|
||||
var list = (IList)initialValue ?? (IList)Activator.CreateInstance(listType);
|
||||
|
||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
var textBox = new TextBox
|
||||
{
|
||||
Text = GetListDisplayText(list),
|
||||
Width = 500,
|
||||
Height = 60,
|
||||
Multiline = true,
|
||||
ReadOnly = true,
|
||||
ScrollBars = ScrollBars.Vertical
|
||||
};
|
||||
var editButton = new Button { Text = "Edit", AutoSize = true };
|
||||
|
||||
editButton.Click += (s, e) =>
|
||||
{
|
||||
using (var listEditor = new ListEditorDialog(elementType, list, this, optionsWindow, name))
|
||||
{
|
||||
if (listEditor.ShowDialog(optionsWindow) == DialogResult.OK)
|
||||
{
|
||||
var newList = listEditor.GetEditedList();
|
||||
list.Clear();
|
||||
foreach (object o in newList)
|
||||
{
|
||||
list.Add(o);
|
||||
}
|
||||
|
||||
textBox.Text = GetListDisplayText(list);
|
||||
|
||||
valueChangedCallback(list);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(list);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, label, textBox, editButton);
|
||||
}
|
||||
|
||||
private string GetListDisplayText(IList list)
|
||||
{
|
||||
if (list == null || list.Count == 0)
|
||||
return "(empty list)";
|
||||
|
||||
var items = new List<string>();
|
||||
foreach (var item in list)
|
||||
{
|
||||
items.Add(item?.ToString() ?? "(null)");
|
||||
}
|
||||
return string.Join(Environment.NewLine, items);
|
||||
}
|
||||
|
||||
private Control CreatePathControl(string name, string description, object initialValue, Action<object> valueChangedCallback,
|
||||
OptionsWindow optionsWindow, string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
@@ -186,3 +250,9 @@ public class DefaultControlFactory : IControlFactory
|
||||
return outerPanel;
|
||||
}
|
||||
}
|
||||
|
||||
// List Editor Dialog
|
||||
|
||||
// Item Editor Dialog
|
||||
|
||||
// Helper class for simple property info
|
||||
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public partial class ItemEditorDialog : Form
|
||||
{
|
||||
private readonly Type _itemType;
|
||||
private object _workingValue;
|
||||
private readonly DefaultControlFactory _controlFactory;
|
||||
private readonly OptionsWindow _parentOptionsWindow;
|
||||
|
||||
private FlowLayoutPanel _propertyPanel;
|
||||
private Button _okButton;
|
||||
private Button _cancelButton;
|
||||
|
||||
public ItemEditorDialog(Type itemType, object initialValue, DefaultControlFactory controlFactory, OptionsWindow parentOptionsWindow)
|
||||
{
|
||||
_itemType = itemType;
|
||||
_workingValue = CloneObject(initialValue);
|
||||
_controlFactory = controlFactory;
|
||||
_parentOptionsWindow = parentOptionsWindow;
|
||||
|
||||
InitializeComponent();
|
||||
CreatePropertyControls();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = $"Edit {_itemType.Name}";
|
||||
this.Size = new System.Drawing.Size(650, 420);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
|
||||
_propertyPanel = new FlowLayoutPanel
|
||||
{
|
||||
Location = new System.Drawing.Point(12, 12),
|
||||
Size = new System.Drawing.Size(620, 320),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
FlowDirection = FlowDirection.TopDown,
|
||||
AutoScroll = true
|
||||
};
|
||||
|
||||
_okButton = new Button
|
||||
{
|
||||
Text = "OK",
|
||||
Location = new System.Drawing.Point(470, 340),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
DialogResult = DialogResult.OK
|
||||
};
|
||||
|
||||
_cancelButton = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
Location = new System.Drawing.Point(550, 340),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
DialogResult = DialogResult.Cancel
|
||||
};
|
||||
|
||||
this.Controls.AddRange(new Control[] { _propertyPanel, _okButton, _cancelButton });
|
||||
}
|
||||
|
||||
private void CreatePropertyControls()
|
||||
{
|
||||
if (_itemType.IsValueType || _itemType == typeof(string))
|
||||
{
|
||||
// For simple types, create a single control
|
||||
CreateSimpleValueControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
// For complex types, create controls for each property
|
||||
CreateComplexObjectControls();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSimpleValueControl()
|
||||
{
|
||||
var dummyProperty = new SimplePropertyInfo(_itemType, "Value");
|
||||
var control = _controlFactory.CreateControl("Value", "Value", dummyProperty, _workingValue,
|
||||
newValue => _workingValue = newValue, _parentOptionsWindow);
|
||||
|
||||
_propertyPanel.Controls.Add(control);
|
||||
}
|
||||
|
||||
private void CreateComplexObjectControls()
|
||||
{
|
||||
var properties = _itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p => p.CanRead && p.CanWrite)
|
||||
.ToArray();
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var currentValue = property.GetValue(_workingValue);
|
||||
var control = _controlFactory.CreateControl(property.Name, property.Name, property, currentValue,
|
||||
newValue => property.SetValue(_workingValue, newValue), _parentOptionsWindow);
|
||||
|
||||
_propertyPanel.Controls.Add(control);
|
||||
}
|
||||
}
|
||||
|
||||
public object GetEditedValue()
|
||||
{
|
||||
return _workingValue;
|
||||
}
|
||||
|
||||
private object CloneObject(object obj)
|
||||
{
|
||||
if (obj == null) return GetDefaultValue(_itemType);
|
||||
|
||||
var type = obj.GetType();
|
||||
if (type.IsValueType || type == typeof(string))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var clone = Activator.CreateInstance(type);
|
||||
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (prop.CanRead && prop.CanWrite)
|
||||
{
|
||||
prop.SetValue(clone, prop.GetValue(obj));
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return GetDefaultValue(type);
|
||||
}
|
||||
}
|
||||
|
||||
private object GetDefaultValue(Type type)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public partial class ListEditorDialog : Form
|
||||
{
|
||||
private readonly Type _elementType;
|
||||
private readonly IList _originalList;
|
||||
private readonly IList _workingList;
|
||||
private readonly DefaultControlFactory _controlFactory;
|
||||
private readonly OptionsWindow _parentOptionsWindow;
|
||||
private readonly string _name;
|
||||
|
||||
private ListBox _listBox;
|
||||
private Button _addButton;
|
||||
private Button _editButton;
|
||||
private Button _removeButton;
|
||||
private Button _okButton;
|
||||
private Button _cancelButton;
|
||||
|
||||
public ListEditorDialog(Type elementType, IList originalList, DefaultControlFactory controlFactory, OptionsWindow parentOptionsWindow, string name)
|
||||
{
|
||||
_elementType = elementType;
|
||||
_originalList = originalList;
|
||||
_controlFactory = controlFactory;
|
||||
_parentOptionsWindow = parentOptionsWindow;
|
||||
_name = name;
|
||||
|
||||
// Create a working copy of the list
|
||||
var listType = typeof(List<>).MakeGenericType(elementType);
|
||||
_workingList = (IList)Activator.CreateInstance(listType);
|
||||
|
||||
foreach (var item in originalList)
|
||||
{
|
||||
_workingList.Add(CloneObject(item));
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
LoadListItems();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = $"Edit {_name}";
|
||||
this.Size = new System.Drawing.Size(600, 400);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
|
||||
// Create controls
|
||||
_listBox = new ListBox
|
||||
{
|
||||
Location = new System.Drawing.Point(12, 12),
|
||||
Size = new System.Drawing.Size(460, 300),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom
|
||||
};
|
||||
|
||||
_addButton = new Button
|
||||
{
|
||||
Text = "Add",
|
||||
Location = new System.Drawing.Point(490, 12),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Right
|
||||
};
|
||||
|
||||
_editButton = new Button
|
||||
{
|
||||
Text = "Edit",
|
||||
Location = new System.Drawing.Point(490, 45),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Right,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
_removeButton = new Button
|
||||
{
|
||||
Text = "Remove",
|
||||
Location = new System.Drawing.Point(490, 78),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Right,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
_okButton = new Button
|
||||
{
|
||||
Text = "OK",
|
||||
Location = new System.Drawing.Point(410, 330),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
DialogResult = DialogResult.OK
|
||||
};
|
||||
|
||||
_cancelButton = new Button
|
||||
{
|
||||
Text = "Cancel",
|
||||
Location = new System.Drawing.Point(490, 330),
|
||||
Size = new System.Drawing.Size(75, 23),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
DialogResult = DialogResult.Cancel
|
||||
};
|
||||
|
||||
// Add event handlers
|
||||
_listBox.SelectedIndexChanged += ListBox_SelectedIndexChanged;
|
||||
_addButton.Click += AddButton_Click;
|
||||
_editButton.Click += EditButton_Click;
|
||||
_removeButton.Click += RemoveButton_Click;
|
||||
|
||||
// Add controls to form
|
||||
this.Controls.AddRange(new Control[] { _listBox, _addButton, _editButton, _removeButton, _okButton, _cancelButton });
|
||||
}
|
||||
|
||||
private void LoadListItems()
|
||||
{
|
||||
_listBox.Items.Clear();
|
||||
foreach (var item in _workingList)
|
||||
{
|
||||
_listBox.Items.Add(item?.ToString() ?? "(null)");
|
||||
}
|
||||
}
|
||||
|
||||
private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
bool hasSelection = _listBox.SelectedIndex >= 0;
|
||||
_editButton.Enabled = hasSelection;
|
||||
_removeButton.Enabled = hasSelection;
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var defaultValue = GetDefaultValue(_elementType);
|
||||
using (var itemEditor = new ItemEditorDialog(_elementType, defaultValue, _controlFactory, _parentOptionsWindow))
|
||||
{
|
||||
if (itemEditor.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
_workingList.Add(itemEditor.GetEditedValue());
|
||||
LoadListItems();
|
||||
_listBox.SelectedIndex = _listBox.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EditButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_listBox.SelectedIndex >= 0)
|
||||
{
|
||||
var selectedItem = _workingList[_listBox.SelectedIndex];
|
||||
using (var itemEditor = new ItemEditorDialog(_elementType, selectedItem, _controlFactory, _parentOptionsWindow))
|
||||
{
|
||||
if (itemEditor.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
_workingList[_listBox.SelectedIndex] = itemEditor.GetEditedValue();
|
||||
LoadListItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_listBox.SelectedIndex >= 0)
|
||||
{
|
||||
int selectedIndex = _listBox.SelectedIndex;
|
||||
_workingList.RemoveAt(selectedIndex);
|
||||
LoadListItems();
|
||||
|
||||
// Maintain selection if possible
|
||||
if (_listBox.Items.Count > 0)
|
||||
{
|
||||
_listBox.SelectedIndex = Math.Min(selectedIndex, _listBox.Items.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IList GetEditedList()
|
||||
{
|
||||
return _workingList;
|
||||
}
|
||||
|
||||
private object GetDefaultValue(Type type)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object CloneObject(object obj)
|
||||
{
|
||||
if (obj == null) return null;
|
||||
|
||||
var type = obj.GetType();
|
||||
if (type.IsValueType || type == typeof(string))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
// For complex objects, try to create a new instance and copy properties
|
||||
try
|
||||
{
|
||||
var clone = Activator.CreateInstance(type);
|
||||
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (prop.CanRead && prop.CanWrite)
|
||||
{
|
||||
prop.SetValue(clone, prop.GetValue(obj));
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return obj; // Fallback to original object if cloning fails
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public partial class OptionsWindow
|
||||
// treeViewCategories
|
||||
//
|
||||
treeViewCategories.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
|
||||
treeViewCategories.HideSelection = false;
|
||||
treeViewCategories.Location = new Point(12, 12);
|
||||
treeViewCategories.Name = "treeViewCategories";
|
||||
treeViewCategories.Size = new Size(200, 582);
|
||||
|
||||
@@ -145,7 +145,10 @@ public partial class OptionsWindow : Form
|
||||
setting.Description,
|
||||
setting.Property,
|
||||
setting.Value,
|
||||
(newValue) => setting.Value = newValue,
|
||||
(newValue) =>
|
||||
{
|
||||
setting.Value = newValue;
|
||||
},
|
||||
this);
|
||||
|
||||
flowLayoutPanelSettings.Controls.Add(control);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
internal class SimplePropertyInfo : PropertyInfo
|
||||
{
|
||||
private readonly Type _propertyType;
|
||||
private readonly string _name;
|
||||
|
||||
public SimplePropertyInfo(Type propertyType, string name)
|
||||
{
|
||||
_propertyType = propertyType;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public override PropertyAttributes Attributes => PropertyAttributes.None;
|
||||
public override bool CanRead => true;
|
||||
public override bool CanWrite => true;
|
||||
public override Type PropertyType => _propertyType;
|
||||
public override string Name => _name;
|
||||
public override Type DeclaringType => typeof(object);
|
||||
public override Type ReflectedType => typeof(object);
|
||||
|
||||
public override MethodInfo[] GetAccessors(bool nonPublic) => new MethodInfo[0];
|
||||
public override MethodInfo GetGetMethod(bool nonPublic) => null;
|
||||
public override ParameterInfo[] GetIndexParameters() => new ParameterInfo[0];
|
||||
public override MethodInfo GetSetMethod(bool nonPublic) => null;
|
||||
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) => obj;
|
||||
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) { }
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[0];
|
||||
public override object[] GetCustomAttributes(bool inherit) => new object[0];
|
||||
public override bool IsDefined(Type attributeType, bool inherit) => false;
|
||||
}
|
||||
Reference in New Issue
Block a user