Click here to Skip to main content
15,886,873 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I've created an ugly looking windows forms application for testing the methods of classes I'm creating for another application. I've got a panel which will be filled with controls for each parameter. For a parameter for the List<t> type I'm using a DataGridView. When I click the test-button, the parameter values will be read from the controls and the selected method of the class will be invoked.
For strings and integers this is working fine, however I'm lost in instantiating the parameter type in case it is a List<t> and filling it from a DataGridView.

What I have tried:

C#
private void btnTestMethod_Click(object sender, EventArgs e)
{
    try
    {
        if (_selectedClass != null && _selectedMethod != string.Empty)
        {
            ConstructorInfo classConstructor = _selectedClass.GetConstructor(Type.EmptyTypes);
            object classObject = classConstructor.Invoke(new object[] { });

            List<object> parameterValues = new List<object>();
            List<Type> parameterTypes = new List<Type>();

            for (int idx = 0; idx < pnlParameters.Controls.Count; idx++)
            {
                string parameterName = pnlParameters.Controls[idx].Name;
                var parameter = _selecedParameters.First(param => param.Name.Equals(parameterName));

                if (parameter != null)
                {
                    object parameterObject;

                    parameterTypes.Add(parameter.ParameterType);

                    switch (Type.GetTypeCode(parameter.ParameterType))
                    {
                        case TypeCode.String:
                            parameterObject = Activator.CreateInstance(typeof(string), new object[] { pnlParameters.Controls[idx].Text.ToCharArray() });
                            break;

                        case TypeCode.Int32:
                            parameterObject = Activator.CreateInstance(typeof(Int32), new object[] { Convert.ToInt32(pnlParameters.Controls[idx].Text) });
                            break;

                        case TypeCode.Int64:
                            parameterObject = Activator.CreateInstance(typeof(Int32), new object[] { Convert.ToInt64(pnlParameters.Controls[idx].Text) });
                            break;

                        case TypeCode.Object:
                            //first check if list<T>
                            //then create an empty list<T>
                            //then check if datagridview
                            //Read each row into list

                            if (parameter.ParameterType.IsGenericType &&
                                parameter.ParameterType.GetGenericTypeDefinition() == typeof(List<>))
                            {
                                // resolve T in List<T>
                                Type type = parameter.ParameterType.GetGenericArguments()[0];

                                // avoid problems with List<Nullable<Something>>
                                type = Nullable.GetUnderlyingType(type) ?? type;

                                // create list
                                //IList objList = (IList)Activator.CreateInstance(type);
                                IList objList = (IList)Activator.CreateInstance(parameter.ParameterType);

                                if (pnlParameters.Controls[idx].GetType() == typeof(DataGridView))
                                {
                                    using (DataGridView dgv = (DataGridView)pnlParameters.Controls[idx])
                                    {
                                        List<string> properties = type.GetProperties().Where(p => p.PropertyType.IsPublic).Select(p => p.Name).ToList();

                                        foreach (DataRow row in dgv.Rows)
                                        {
                                            var obj = Activator.CreateInstance(parameter.ParameterType);
                                            foreach (string colName in properties)
                                            {

                                            }
                                            objList.Add(obj);
                                        }

                                    }

                                }

                            }
                            parameterObject = Activator.CreateInstance(parameter.ParameterType);
                            break;

                        default:
                            parameterObject = Activator.CreateInstance(parameter.ParameterType);
                            break;
                    }
                    parameterValues.Add(parameterObject);
                }
            }

            //Now invoke the method and retrieve the result
            MethodInfo classMethod = _selectedClass.GetMethod(_selectedMethod, parameterTypes.ToArray());
            object testResult = classMethod.Invoke(classObject, parameterValues.ToArray());

            //Show result in treeview
            PopulateResults(testResult);
        }
    }
    catch (Exception ex)
    {
        ShowErrorMessage(ex);
    }

    ResetParameters();
}
Posted
Updated 24-Jun-23 23:45pm
Comments
PIEBALDconsult 16-Jun-23 13:18pm    
I think you'll need to make an array which contains the List.
classMethod.Invoke ( classObject , new object[] { parameterValues } ) ;
Member 11728887 20-Jun-23 8:22am    
Thanks

1 solution

I was a bit impatient (and lazy) and asked ChatGPT (AI). Together we came up with the following solution:
C#
private void btnTestMethod_Click(object sender, EventArgs e)
{
	InvokeMethod();
}

private void InvokeMethod()
{
	try
	{
		if (_selectedClass != null && !string.IsNullOrEmpty(_selectedMethod))
		{
			ConstructorInfo classConstructor = _selectedClass.GetConstructor(Type.EmptyTypes);
			object classObject = classConstructor.Invoke(new object[] { });

			List<object> parameterValues = new List<object>();
			List<Type> parameterTypes = new List<Type>();

			for (int idx = 0; idx < pnlParameters.Controls.Count; idx++)
			{
				string parameterName = pnlParameters.Controls[idx].Name;
				var parameter = _selectedParameters.FirstOrDefault(param => param.Name.Equals(parameterName));

				if (parameter != null)
				{
					object parameterObject;

					parameterTypes.Add(parameter.ParameterType);

					switch (Type.GetTypeCode(parameter.ParameterType))
					{
						case TypeCode.String:
							parameterObject = pnlParameters.Controls[idx].Text;
							break;

						case TypeCode.Int16:
							if (short.TryParse(pnlParameters.Controls[idx].Text, out short shortValue))
								parameterObject = shortValue;
							else
								throw new ArgumentException("Invalid Int16 value for parameter: " + parameter.Name);
							break;

						case TypeCode.Int32:
							if (int.TryParse(pnlParameters.Controls[idx].Text, out int intValue))
								parameterObject = intValue;
							else
								throw new ArgumentException("Invalid Int32 value for parameter: " + parameter.Name);
							break;

						case TypeCode.Int64:
							if (long.TryParse(pnlParameters.Controls[idx].Text, out long longValue))
								parameterObject = longValue;
							else
								throw new ArgumentException("Invalid Int64 value for parameter: " + parameter.Name);
							break;

						case TypeCode.Boolean:
							switch (pnlParameters.Controls[idx])
							{
								case CheckBox checkBox:
									parameterObject = checkBox.Checked;
									break;

								case TextBox textBox:
								default:
									if (bool.TryParse(pnlParameters.Controls[idx].Text, out bool boolValue))
										parameterObject = boolValue;
									else
										throw new ArgumentException("Invalid Boolean value for parameter: " + parameter.Name);
									break;
							}
							break;

						case TypeCode.Object:
							if (parameter.ParameterType.IsGenericType &&
								parameter.ParameterType.GetGenericTypeDefinition() == typeof(List<>))
							{
								Type elementType = parameter.ParameterType.GetGenericArguments()[0];

								if (pnlParameters.Controls[idx] is DataGridView dataGridView)
								{
									IList listObject = (IList)Activator.CreateInstance(parameter.ParameterType);

									foreach (DataGridViewRow row in dataGridView.Rows)
									{
										if (!row.IsNewRow)
										{
											object elementObject = Activator.CreateInstance(elementType);
											
											foreach (DataGridViewCell cell in row.Cells)
											{
												string columnName = cell.OwningColumn.Name;
												PropertyInfo property = elementType.GetProperty(columnName);

												if (property != null)
												{
													object cellValue = cell.Value;

													switch (Type.GetTypeCode(property.PropertyType))
													{
														case TypeCode.String:
															property.SetValue(elementObject, cellValue);
															break;

														case TypeCode.Int16:
															if (short.TryParse(cellValue.ToString(), out short shortCellValue))
																property.SetValue(elementObject, shortCellValue);
															else
																throw new ArgumentException("Invalid Int16 value for parameter: " + parameter.Name);
															break;
														case TypeCode.Int32:
															if (int.TryParse(cellValue.ToString(), out int intCellValue))
																property.SetValue(elementObject, intCellValue);
															else
																throw new ArgumentException("Invalid Int32 value for parameter: " + parameter.Name);
															break;
														case TypeCode.Int64:
															if (long.TryParse(cellValue.ToString(), out long lngCellValue))
																property.SetValue(elementObject, lngCellValue);
															else
																throw new ArgumentException("Invalid Int64 value for parameter: " + parameter.Name);
															break;
														case TypeCode.Boolean:
															if (bool.TryParse(cellValue.ToString(), out bool blnCellValue))
																property.SetValue(elementObject, blnCellValue);
															else
																throw new ArgumentException("Invalid Boolean value for parameter: " + parameter.Name);
															break;
														case TypeCode.Object:
															if (property.PropertyType.IsEnum)
															{
																if (cellValue != null)
																{
																	object enumValue = Enum.Parse(property.PropertyType, cellValue.ToString());
																	property.SetValue(elementObject, enumValue);
																}
																else
																{
																	throw new ArgumentException("Invalid Enum value for parameter: " + parameter.Name);
																}
															}
															else
															{
																property.SetValue(elementObject, cellValue);
															}
															break;
														default:
															property.SetValue(elementObject, cellValue);
															break;
													}
												}
											}

											listObject.Add(elementObject);
										}
									}

									parameterObject = listObject;
								}
								else
								{
									throw new ArgumentException("Invalid control type for List parameter: " + parameter.Name);
								}
							}
							else
							{
								throw new ArgumentException("Unsupported object type for parameter: " + parameter.Name);
							}
							break;

						default:
							throw new ArgumentException("Unsupported parameter type: " + parameter.ParameterType.Name);
					}

					parameterValues.Add(parameterObject);
				}
			}

			MethodInfo classMethod = _selectedClass.GetMethod(_selectedMethod, parameterTypes.ToArray());
			object result = classMethod.Invoke(classObject, parameterValues.ToArray());

			DisplayResults(result);
		}
	}
	catch (Exception ex)
	{
		ShowErrorMessage(ex);
	}
	//ResetParameters();
}

private void DisplayResults(object result)
{
	// Clear previous results
	pnlResults.Controls.Clear();
	Type resultType = result.GetType();

	switch (result)
	{
		case null:
			// No results to display
			Label lblNoResults = new Label();
			lblNoResults.Text = "No results available.";
			lblNoResults.Dock = DockStyle.Fill;
			pnlResults.Controls.Add(lblNoResults);
			break;

		case string strResult:
			// Single string result
			TextBox txtResult = new TextBox();
			txtResult.Text = strResult;
			txtResult.ReadOnly = true;
			txtResult.Dock = DockStyle.Fill;
			pnlResults.Controls.Add(txtResult);
			break;

		case ICollection collectionResult:
			// Collection of items
			if (resultType.IsGenericType)
			{
				TreeView tvCollection = XmlDocumentToTreeView(result);
				tvCollection.Dock = DockStyle.Fill;
				pnlResults.Controls.Add(tvCollection);
			}
			else
			{
				ListBox lstResult = new ListBox();
				lstResult.Items.AddRange(collectionResult.Cast<object>().ToArray());
				lstResult.Dock = DockStyle.Fill;
				pnlResults.Controls.Add(lstResult);
			}
			break;

		case DataTable dataTableResult:
			// Tabular data
			DataGridView dgvResult = new DataGridView();
			dgvResult.DataSource = dataTableResult;
			dgvResult.Dock = DockStyle.Fill;
			pnlResults.Controls.Add(dgvResult);
			break;

		case TreeNode treeNodeResult:
			// Hierarchical data
			TreeView tvResult = new TreeView();
			tvResult.Nodes.Add(treeNodeResult);
			tvResult.Dock = DockStyle.Fill;
			pnlResults.Controls.Add(tvResult);
			break;

		default:
			// Other types of results including custom objects
			Control resultControl = CreateObjectControl(result);
			if (resultControl != null)
			{
				resultControl.Dock = DockStyle.Fill;
				pnlResults.Controls.Add(resultControl);
			}
			else
			{
				// Unsupported result type
				Label lblUnsupported = new Label();
				lblUnsupported.Text = "Result type not supported for display.";
				lblUnsupported.Dock = DockStyle.Fill;
				pnlResults.Controls.Add(lblUnsupported);
			}
			break;
	}
}

private Control CreateObjectControl(object result)
{
	Type resultType = result.GetType();

	// Check if the result is a custom class with properties
	if (resultType.IsClass && !resultType.IsPrimitive && !resultType.IsArray)
	{
		// Create a panel to hold the property controls
		Panel resultPanel = new Panel();
		resultPanel.AutoScroll = true;

		// Get all public instance properties of the result object
		PropertyInfo[] properties = resultType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

		int yOffset = 0;
		foreach (PropertyInfo property in properties)
		{
			Label lblPropertyName = new Label();
			lblPropertyName.Text = property.Name;
			lblPropertyName.Location = new Point(0, yOffset);
			resultPanel.Controls.Add(lblPropertyName);

			TextBox txtPropertyValue = new TextBox();
			object propertyValue = property.GetValue(result);
			txtPropertyValue.Text = propertyValue?.ToString() ?? "null";
			txtPropertyValue.Location = new Point(100, yOffset);
			txtPropertyValue.ReadOnly = true;
			resultPanel.Controls.Add(txtPropertyValue);

			yOffset += 30; // Adjust the vertical spacing as needed
		}

		return resultPanel;
	}

	return XmlDocumentToTreeView(result); // Serialize custom objects to XML
}

private TreeView XmlDocumentToTreeView(object result)
{
	TreeView tvResult = new TreeView();
	tvResult.Dock = DockStyle.Fill;

	XmlDocument xmlDoc = new XmlDocument();

	xmlDoc.PreserveWhitespace = true;
	xmlDoc.LoadXml(SerializeToXml(result));

	TreeNode rootNode = CreateTreeNode(xmlDoc.DocumentElement);
	tvResult.Nodes.Add(rootNode);

	return tvResult;
}

public string SerializeToXml(object obj)
{
	XmlSerializer serializer = new XmlSerializer(obj.GetType());

	using (StringWriter writer = new StringWriter())
	{
		serializer.Serialize(writer, obj);
		return writer.ToString();
	}
}

private TreeNode CreateTreeNode(XmlNode xmlNode)
{
	TreeNode treeNode = new TreeNode(xmlNode.Name);

	foreach (XmlNode childNode in xmlNode.ChildNodes)
	{
		if (childNode.NodeType == XmlNodeType.Element)
		{
			TreeNode childTreeNode = CreateTreeNode(childNode);
			treeNode.Nodes.Add(childTreeNode);
		}
		else if (childNode.NodeType == XmlNodeType.Text)
		{
			treeNode.Nodes.Add(childNode.InnerText);
		}
	}

	return treeNode;
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900