I'm having trouble with creating a plugin solution with reflection. When I click on a menuItem I would like to load and show another program in my window. I figured I would need 3 projects.
Client program that wants to load another program when clicked on menuItem
Program that I want to load
Plugin class
Code client program
private void nQueensToolStripMenuItem_Click(object sender, EventArgs e)
{
// Create an assembly object to load our classes
string path = Application.StartupPath + "\\NQueens.dll";
Assembly ass = Assembly.LoadFile(path);
Type objType = ass.GetType("NQueens.NQueen");
// Create an instace of NQueens.NQueen
var instance = Activator.CreateInstance(objType);
// public static bool berekenQueens()
objType.InvokeMember("berekenQueens",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static,
null, instance, null);
// private static bool bordValidatie
objType.InvokeMember("bordValidatie",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static,
null, instance, null);
}
Class
[AttributeUsage(AttributeTargets.Class)]
public class myPluginAttribute : Attribute
{
private bool _bIsPlugin;
public Boolean IsPlugin
{
get { return _bIsPlugin; }
set { _bIsPlugin = value; }
}
private string _sDescription;
public string Description
{
get { return _sDescription; }
set { _sDescription = value; }
}
public myPluginAttribute(Boolean b, String desc)
{
IsPlugin = b;
Description = desc;
}
}
NQueens (program i want to load when clicked on menuitem)
NQueens.cs
public class NQueen
{
public static bool berekenQueens(int Row, int N, bool[,] bord)
{
if (Row >= N) return true;
for (int Col = 0; Col < N; Col++)
{
//Q toevoegen
bord[Row, Col] = true;
//Q + Q volgende Row controleren
if (bordValidatie(Row, Col, bord, N) && berekenQueens(Row + 1, N, bord))
{
return true;
}
//Q verwijderen indien niet door controle
bord[Row, Col] = false;
}
return false;
}
private static bool bordValidatie(int currentRow, int currentCol, bool[,] currentBord, int N)
{
int colstep = 1;
for (int i = currentRow - 1; i >= 0; i--)
{
//rechte lijn
if (currentBord[i, currentCol])
return false;
//linker diagonaal
if (currentCol - colstep >= 0)
{
if (currentBord[i, currentCol - colstep])
return false;
}
//rechter diagonaal
if (currentCol + colstep < N)
{
if (currentBord[i, currentCol + colstep])
return false;
}
colstep += 1;
}
return true;
}
}
MainWindow (nqueens)
public partial class MainWindow : Window
{
public int iN { get { return Convert.ToInt32(txtN.Text); } set { txtN.Text = "" + value; } }
private bool[,] spelbord;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
spelbord = new bool[iN, iN];
NQueen.berekenQueens(0, iN, spelbord);
visualise(iN, spelbord);
}
private void visualise(int N, bool[,] bord)
{
gridTekenen();
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++)
{
Rectangle rect = new Rectangle();
rect.Stretch = Stretch.Fill;
TextBlock txtB = new TextBlock();
if (spelbord[row, col])
{
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Colors.LightGreen;
rect.Fill = mySolidColorBrush;
txtB.Text = "Q";
}
rect.SetValue(Grid.ColumnProperty, col);
rect.SetValue(Grid.RowProperty, row);
txtB.SetValue(Grid.ColumnProperty, col);
txtB.SetValue(Grid.RowProperty, row);
gridPaneel.Children.Add(rect);
gridPaneel.Children.Add(txtB);
}
}
}
private void gridTekenen()
{
gridPaneel.ShowGridLines = true;
int grooteGrid = int.Parse(txtN.Text);
RowDefinition rowDef;
ColumnDefinition colDef;
for (int i = 0; i < grooteGrid; i++)
{
rowDef = new RowDefinition();
GridLengthConverter myGridLengthConverter = new GridLengthConverter();
GridLength gl1 = (GridLength)myGridLengthConverter.ConvertFromString(150 + "*");
rowDef.Height = gl1;
colDef = new ColumnDefinition();
colDef.Width = gl1;
gridPaneel.RowDefinitions.Add(rowDef);
gridPaneel.ColumnDefinitions.Add(colDef);
}
}
}
I'm stuck trying to get this thing working. How can I make my program to show the NQueens program? How does my client program know the methods from NQueen and use them?
EDIT: I made some changes in the nQueensToolStripMenuItem_Click method. Is this correct now? How can I show the NQueens method in my window now?
As far as I know, you will need a common Interface, which helps communicating with the "Extensions". You will then have three projects:
Your Core Application
A Library just containing the Interface (i.e. IPlugin)
Your Extensions.
Have a look at codeproject to get a better idea about the architecture of plugins.
A much simpler pattern is the classic "Factory Pattern" with interfaces. I've done this before to create plug-ins and it's really straightforward:
1) Create an interface containing all the properties and methods you would ever use to "talk" to the plug-in. Define this in a base class/assembly that will (hardly) ever change.
2) Create a frame UI application which perhaps has one stock implementation of the plug-in interface, perhaps the default menus, so you can easily debug and get your interface right.
3) Now to the dynamic loading... Create a configuration string listing a number of files which should be loaded. Good to allow this to be configurable rather than hard-coding paths, also for security never just load assemblies in a path, you don't know who could have put them there via a back door in file permissions.
4) On start-up use the assembly load from methods to go through each file and once loaded use reflection to query the interfaces, looking for your plugin interface. Store a collection of loaded interfaces/plug-ins and use them as you wish.
Obviously as you have a UI you will have a method to create the child menu items and windows and set their parent to your frame applications menu or client area appropriately. Then you will have some events defined on the interface to deal with events like items being clicked. Makes sense to also define a standard "tool" or command interface, so you can deal with menu items and toolbar items the same way.
Related
I have a class called Board. It has multiple columns and you can add Stones to a column by Calling AddStoneToColumn(column c). The method shouldnt modify the objet itselft but only create a new Board Object with the added stone but somehow it keeps changing itself too.
Heres my relevant Code:
public Board AddStoneToColumn(int column)
{
Board resultBoard = new Board(this);
Boolean isPlaced = false;
for (int i = 0; i < height; i++)
{
if (resultBoard.GetStone(column, i) == StoneEnum.EMPTY)
{
resultBoard.SetExactCoords(column, i, turn);
isPlaced = true;
break;
}
}
if (!isPlaced)
{
throw new InvalidOperationException("Spalte voll");
}
if (turn == StoneEnum.BLUE)
resultBoard.turn = StoneEnum.RED;
if (turn == StoneEnum.RED)
resultBoard.turn = StoneEnum.BLUE;
return resultBoard;
}
private void SetExactCoords(int x, int y, StoneEnum stone)
{
if (stone == StoneEnum.EMPTY)
throw new NotSupportedException("Empty stone ??");
this.stones[x, y] = stone;
}
public Board(Board board)
{
this.stones = board.stones;
this.turn = board.turn;
}
You are only copying references in your cloning constructor, not creating a copy of the data. You will have to duplicate those arrays/collections.
Maybe you can use a struct instead of a class?
But for your example, i would use
ICloneable Interface.
Also, you can watch this discussion too.
I would like to redo the code of my old windows forms application on wpf and I have a problem with referencing datagridview.
This is the void look of my old application:
private void button2_Click(object sender, EventArgs e)
{
if (DGV1.Rows.Count > 0 && DGV1.SelectedRows != null)
{
bool wart = true;
for (int i = 0; i < listBox2.Items.Count; i++)
{
listBox2.SelectedIndex = i;
int w1 = Int32.Parse(listBox2.SelectedItem.ToString());
int w2 = Int32.Parse(DGV1.SelectedRows[0].Cells[0].Value.ToString());
if (w1 == w2)
{
wart = false;
break;
}
}
if (wart)
{
listBox2.Items.Add(DGV1.SelectedRows[0].Cells[0].Value);
}
}
}
This is the void look of my new application:
private void Button1_Click(object sender, RoutedEventArgs e)
{
IList rows = dataGrid1.SelectedItems;
if(dataGrid1.SelectedItem != null)
{
bool wart = true;
for (int i =0; i < listBox1.Items.Count; i++)
{
listBox1.SelectedIndex = i;
object item = dataGrid1.SelectedItem;
int w1 = Int32.Parse(listBox1.SelectedItem.ToString());
int w2 = Int32.Parse(dataGrid1.SelectedCells[0].Column.GetCellContent(item).ToString()); <--- !!
if(w1 == w2)
{
wart = false;
break;
}
}
if(wart)
{
listBox1.Items.Add(dataGrid1.SelectedCells[0]); <-- !!
}
}
}
The application spills out at the second if, where it displays:
And it should be:
Please Help :-)
It should probably be like this:
listBox1.Items.Add(dataGrid1.CurrentRow.Cells[0].Value);
This code is from WinForms, but I assume the coding for wpf may not be different, since both are in c#.
dataGrid1.SelectedItem isn't just some object, it has concrete type and properties like Id, Tytul, Kategorie, Text
you need to make a cast to that concrete type and access property instead of trying to get the value from low-level UI elements like DataGridCellInfo:
var item = (MyConcreteClass)dataGrid1.SelectedItem;
int w2 = item.Id;
I'm trying to simulate a user pressing ctrl down, the main goal would be in a datagridview when I select something programarly (initially) I dont want the user to then change that selection if not just add on to it or subtract, just as if you were to hold ctrl + left mouse click. I have no idea where to even begin. I tried to create a selection change event conbined with logicals but that will cause an infinite loop since we would be selecting one by a user then the code change other and other etc infinitely triggering that event. Please help, I'm sort of new to coding. I also don't know how to determine whether a ctrl key has been pressed, is pressed and being held.
private void selecttionh(object sender, EventArgs e)
{
if (stage == "4A" || stage == "3B" && ModifierKeys.HasFlag(Keys.Control))
{
int nothing = 0;
btnclickercl bt = new btnclickercl();
bt.dataGridView1_SelectionChanged(sender, e, dataGridViewReslist, dataGridViewnewres, nothing);
}
if (stage == "4A" || stage == "3B" && (ModifierKeys & Keys.Control) != Keys.Control)
{
MessageBox.Show("Please Press and hold " + "'ctrl'" + " to continue");
dataGridViewReslist.ClearSelection();
for (int i = 0; i < ResRoomSelections.Count; i++)
{
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[ResRoomSelections[i][1]].Selected = true;
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[(ResRoomSelections[i][1]) + 1].Selected = true;
}
}
else
{
dataGridViewReslist.ClearSelection();
for (int i = 0; i < ResRoomSelections.Count; i++)
{
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[ResRoomSelections[i][1]].Selected = true;
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[(ResRoomSelections[i][1]) + 1].Selected = true;
}
}
}
The way to make this happen is to store the selection state separately, update it when the user clicks a cell, then re-apply it. This prevents the selection from being lost every time they click. If you hook the proper event handlers (mouseup, not click) you can do this without the screen flickering and otherwise being a mess to look at.
Everything you need is in this class, including an extension method SetupToggledSelectionMode(), which is your entry point.
static public class Example
{
static private bool[][] GetSelectionState(DataGridView input)
{
int rowCount = input.Rows.Count;
int columnCount = input.Columns.Count;
var result = new bool[rowCount][];
for (var r = 0; r < rowCount; r++)
{
result[r] = new bool[columnCount];
for (var c = 0; c < columnCount; c++)
{
var cell = input.Rows[r].Cells[c];
result[r][c] = cell.Selected;
}
}
return result;
}
static private void SetSelectionState(DataGridView input, bool[][] selectionState)
{
for (int r = 0; r <= selectionState.GetUpperBound(0); r++)
{
for (int c = 0; c <= selectionState[r].GetUpperBound(0); c++)
{
input.Rows[r].Cells[c].Selected = selectionState[r][c];
}
}
}
static public void SetupToggledSelectionMode(this DataGridView input)
{
bool[][] selectionState = GetSelectionState(input); //This will be stored in a closure due to the lambda expressions below
input.CellMouseUp += (object sender, DataGridViewCellMouseEventArgs e) =>
{
selectionState[e.RowIndex][e.ColumnIndex] = !selectionState[e.RowIndex][e.ColumnIndex];
SetSelectionState(input, selectionState);
};
input.SelectionChanged += (object sender, EventArgs e) =>
{
if (selectionState != null)
{
SetSelectionState(input, selectionState);
}
};
}
}
To use, populate your gridview, set up the initial selection programmatically, and call it like this:
myDataGrid.DataSource = myData;
myDataGrid.Refresh();
myDataGrid.SelectAll();
myDataGrid.SetupToggledSelectionMode();
The SetupToggledSelectionMode() method will register the necessary event handlers and store the selection state of the grid in a closed variable accessible to both handlers. So you won't have to declare anything additional; just call the method.
Thank you for this, this really helped me. all I did was to make it more efficient.Since it would call the Selection change every step of the way, so I got rid of that event completely and only kept the CellMouseup Event.
static private bool[][] GetSelectionState(DataGridView input)
{
int rowCount = input.Rows.Count;
int columnCount = input.Columns.Count;
var result = new List<int[]>();
for (var r = 0; r < rowCount; r++)
{
for (var c = 0; c < columnCount; c++)
{
if(input.Rows[r].Cells[c].Selected==true)
{
result.add(new int[]{r,c});//will keep only the integer of selected items
}
}
}
return result;//this for me was a recycled variable it can be used or recycled from somewhere else
}
private void SetSelectionState(DataGridView input,result)
{
for (int i=0;i<result.Count;i++)
{
input.Rows[result[i][0]].Cells[result[i][1]].Selected = true;
}
}
public void SetupToggledSelectionMode(DataGridView input,result)
{
for (int i=0;i<result.Count;i++)
{
if(result[i].SequenceEqual(new int[] { e.RowIndex, e.ColumnIndex }))
{
result.RemoveAt(i);
continueer = 1;
break;
}
}
if (continueer == 0)
{
ResRoomSelections.Add(new int[] { e.RowIndex, e.ColumnIndex });
}
SetSelectionState(input);
//whatever else you need to do
}
I know there is still a better way to search List but I could not get a lamda search to work so I just used brute force
-Thank you John Wu for all
How to Clone or Serialize a Windows Forms Control?
When I am trying to Clone windows forms controls using this code "CloneControl(Control ct1)", it allows me to duplicate controls with some Serializable properties, not with all properties.
public Form1()
{
InitializeComponent();
Columns = new DataGridViewTextBoxColumn[2];
for (int i = 0; i < 2; i++)
{
Columns[i] = new System.Windows.Forms.DataGridViewTextBoxColumn();
//
// Columns[i]
//
Columns[i].HeaderText = "j" + (i + 1);
Columns[i].Name = "Column" + (i + 1);
Columns[i].Width = 50;
}
dataGridView1 = new System.Windows.Forms.DataGridView();
dataGridView1.Name = "dataGridView1";
dataGridView1.Location = new System.Drawing.Point(100, 100);
dataGridView1.RowHeadersWidth = 50;
dataGridView1.RowTemplate.Height = 25;
dataGridView1.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Columns.AddRange(Columns);
dataGridView1.TabIndex = 3;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView1.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView1.Rows[0].Cells[0].Value = "value1";
Controls.Add(dataGridView1);
Control cloned1 = CloneControl(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
}
public Control CloneControl(Control ct1)
{
Hashtable PropertyList = new Hashtable();
PropertyDescriptorCollection Properties = TypeDescriptor.GetProperties(ct1);
Assembly controlAsm = Assembly.LoadWithPartialName(ct1.GetType().Namespace);
Type controlType = controlAsm.GetType(ct1.GetType().Namespace + "." + ct1.GetType().Name);
Control cloned1 = (Control)Activator.CreateInstance(controlType);
foreach (PropertyDescriptor pr1 in Properties)
{
if (pr1.PropertyType.IsSerializable)
{
PropertyList.Add(pr1.Name, pr1.GetValue(ct1));
}
if (PropertyList.Contains(pr1.Name))
{
try
{
Object obj = PropertyList[pr1.Name];
pr1.SetValue(cloned1, obj);
}
catch (Exception ex)
{
}
}
}
return ct2;
}
If you run the code... the you will get
As you can see in the main method I create a clone of dataGridView1, which has a few properties.
And actually each cell value is null in a cloned dataGridView.
Also size of a columns are not cloned!
You may have a question: if Visual Studio or SharpDeveloper as IDE which is written in C# can handle this problem, then it might be possible to write that kind of code! Right?
In Visual Studio When you are trying drag and drop controls, or copy and paste controls, it not only duplicates that controls with all properties (including Serializable or non-Serializable) but also it changes the name of control itself from "dataGridView1" to "dataGridView2" as well as in SharpDeveloper!
What should I do?
What kind of method should I create?
Maybe another control has a many non-Serializable properties!
How to duplicate all of them?
Please anyone.....
Like #Hans mentioned in the comment, Clone is not that easy. If you want to get some identical controls with only a bit different, you'd better use a function to define general behavior and pass the different properties in as parameters. For example, we define a function with some general properties which apply to DataGridView:
private void InitDataGridView(DataGridView dataGridView, string name)
{
dataGridView.Name = name;
// configure other properties here
dataGridView.Location = new System.Drawing.Point(100, 100);
dataGridView.RowHeadersWidth = 50;
dataGridView.RowTemplate.Height = 25;
dataGridView.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
// remember to initialize your columns, or pass it in as a parameter
dataGridView.Columns.AddRange(Columns);
dataGridView.AllowUserToAddRows = false;
dataGridView.Rows.Add();
dataGridView.Rows.Add();
dataGridView.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView.Rows[0].Cells[0].Value = "value1";
}
public Form1()
{
InitializeComponent();
var dataGridView1 = new DataGridView();
var dataGridView2 = new DataGridView();
InitDataGridView(dataGridView1, "dataGridView1");
InitDataGridView(dataGridView2, "dataGridView2");
}
IDE (e.g. Visual Studio) is using PropertyDescriptors, DesignerSerializationVisibility and ShouldSerializeValue, but DataGrid Rows are something special, because you cannot add them at design time! IDE cannot copy something that is not there, so, the solution must be different (if you want to clone controls beyond what IDE/Designer can do - see other answers and comments for this). Try my code (everything except grid rows got cloned without the extra check - the columns got cloned).
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
Note: The above can be done better (by check for the class at the end), but is as it is to be obvious.
using System;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CloneControls {
public partial class Form1: Form {
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
foreach(Control c in splitContainer1.Panel1.Controls)
splitContainer1.Panel2.Controls.Add((Control)Clone(c));
}
static object Clone(object o) {
return Copy(o, Activator.CreateInstance(o.GetType()));
}
static object Copy(object src, object dst) {
IList list = src as IList;
if(list != null) {
IList to = dst as IList;
foreach(var x in list)
to.Add(Clone(x));
return dst; }
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
switch(pd.SerializationVisibility) {
default: continue;
case DesignerSerializationVisibility.Visible:
if(pd.IsReadOnly) continue;
pd.SetValue(dst, pd.GetValue(src));
continue;
case DesignerSerializationVisibility.Content:
Copy(pd.GetValue(src), pd.GetValue(dst));
continue;
}
}
return dst;
}
static void CopyDataGridRows(DataGridView src, DataGridView dst) {
foreach(DataGridViewRow row in src.Rows)
if(!row.IsNewRow) dst.Rows.Add((DataGridViewRow)Clone(row));
}
}
}
I think I made more better method here.
This Method at first checks interface of property: if it is ICollection then it does the first job.
After this one loop ends in the method "DeepClone()", then it is necessary to do another loop without checking PropertyType Interface... I mean I could not mix these two operation into one loop?!
Also You can detect that there will be some kind of Run-time Exceptions and for this reason I put this code into try-catch block...
Control cloned1 = (Control)DeepClone(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
public dynamic DeepClone(dynamic ob1)
{
dynamic ob2 = null;
if (ob1.GetType().IsSerializable && !ob1.GetType().IsArray)
{
if (ob1 != null)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, ob1);
ms.Position = 0;
ob2 = formatter.Deserialize(ms);
}
}
}
else
{
if (ob1.GetType().IsArray)
{
var r1 = ob1.Rank;
object[] d1 = new object[r1];
long[] V1 = new long[r1];
for (int i = 0; i < r1; i++)
{
V1[i] = 0;
d1[i] = ob1.GetUpperBound(i) + 1;
}
ob2 = Activator.CreateInstance(ob1.GetType(), d1);
for (long i = 0; i <= ob2.Length; i++)
{
ob2.SetValue(DeepClone(ob1.GetValue(V1)), V1);
for (int j = 0; j <= V1.GetUpperBound(0); j++)
{
if (V1[j] < ob2.GetUpperBound(j))
{
V1[j]++;
break;
}
else
{
V1[j] = 0;
}
}
}
}
else
{
PropertyInfo[] P1 = ob1.GetType().GetProperties();
ob2 = Activator.CreateInstance(ob1.GetType());
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.GetInterface("System.Collections.ICollection", true) != null)
{
dynamic V2 = p1.GetValue(ob1) as IEnumerable;
MethodInfo gm1 = p1.PropertyType.GetMethods().Where(m => m.Name == "Add").Where(p => p.GetParameters().Count() == 1).First(f => V2[0].GetType().IsSubclassOf(f.GetParameters()[0].ParameterType) || f.GetParameters()[0].ParameterType == V2[0].GetType());
if (V2[0].GetType().IsSubclassOf(gm1.GetParameters()[0].ParameterType) || gm1.GetParameters()[0].ParameterType == V2[0].GetType())
{
for (int i = 0; i < V2.Count; i++)
{
dynamic V3 = DeepClone(V2[i]);
gm1.Invoke(p1.GetValue(ob2), new[] {V3});
}
}
}
}
catch (Exception ex)
{
}
}
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.IsSerializable && p1.CanWrite)
{
var v2 = p1.GetValue(ob1);
p1.SetValue(ob2, v2);
}
if (!p1.PropertyType.IsSerializable && p1.CanWrite)
{
dynamic V2 = p1.GetValue(ob1);
if (p1.PropertyType.GetMethod("Clone") != null)
{
dynamic v1 = V2.Clone();
p1.SetValue(ob2, v1);
}
}
}
catch (Exception ex)
{
}
}
}
}
return ob2;
}
You may say that this Method does not copy some kind of property, But it does copy of main properties and the Cloned control will look like an original control!
Trying to clone a control is overkill except if you really need a totally generic control clone method. Most of the time, you only need to clone a specific control and you have an easy access to the code that created it (see the Form designer generated code, and the setup code you wrote yourself).
But nevertheless, I once used a trick to duplicate many controls at once in order to fill the new tabs of a TabControl, choosing one out of ten tab designs.
I also wanted to use the Form design tool of the C# IDE to edit and modify the 10 template.
So, besides my Tab control form, and using the VS IDE, I created 10 "control factory dummy forms" in my project. I put a dummy Panel control in each of it.
Each time I had to dynamically create a new Tab, I simply instantiated a new dummy window of the desired style. Then I simply moved the Parent pane to my ControlTab (using the Controls.Add() method of the new tab).
This way, you must link the event handlers after the Tab creation (after the controls move). And the event handler's code should be written in you main window class, otherwise you will have "this" reference problems.
Obviously, you will have to store control references somewhere, to be able to access them. The easiest way to do this is to just keep track of each "dummy template Form" you instantiate and to set the "modifier" of your controls to be "public". You can use the Tag property of the destination tab page to store that reference. But, to avoid many casts, it is better to declare an array of each form class, and to store the references there.
I have a class called "Player" with a constructor that takes 2 strings and an int.
These are declared in Textboxes on a form, with each team (Home H / Away A) having a different name, and each name type (Last L / First F) adding to the textbox's name. therefor giving a unique name such as txtFHome1.
In a foreach loop I want to create a new Player with the details of the textboxes on a page.
This is what I have got so far.
List <Player> HomeTeam = new List<Player>
private void btnAccept_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
for (int i = 0; i < 10; i++)
{
HomeTeam.Add (new Player(c.Name.EndsWith("FHome"+i),c.Name.EndsWith("LHome"+i),c.Name.EndsWith("upDownH"+i)));
}
}
}
any help?
From you post I understand that there are always 3 controls for 11 players, so there is no need to iterate trough all Controls in the form.
private void btnAccept_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var player = new Player(((TextBox)this.Controls.FindControl("FHome" + i)).Text, ((TextBox)this.Controls.FindControl("LHome" + i)).Text, ((NumericUpDown)this.Controls.FindControl("upDownH" + i)).Value);
HomeTeam.Add(player);
}
}
The first way is to use dictionaries with all controls. It is the fastest and easiest way.
Dictionary<int, TextBox> FHome;
Dictionary<int, TextBox> LHome;
Dictionary<int, TextBox> upDownH;
You should add the controls like this:
FHome.Add(0, textBoxLHome0);
FHome.Add(1, textBoxLHome1);
Then in your code you can use
for (int i = 0; i < 10; i++)
{
HomeTeam.Add (new Player(FHome[i].Text, LHome[i].Text, upDownH[i].Text));
}
try this:
Controls.OfType<TextBox>().ToList().ForEach((textbox) =>
{
for (int i = 0; i < 10; i++)
{
textbox.Text = "your text";
}
});
remember to include using System.Linq
I advice you to at lease try an encapsulate the three textboxes into a single UserControl like so:
public partial class ControlPlayerParams : UserControl {
public string Param1 { get { return this.textBox1.Text; } }
public string Param2 { get { return this.textBox2.Text; } }
public string Param3 { get { return this.textBox3.Text; } }
public ControlPlayerParams() {
this.InitializeComponent();
}
}
That way, you could at least do what you wanted to do more fluently and more safely (plus you get to modify just one UserControl in case you need something changed (Validation ??)):
foreach (ControlPlayerParams cpp in this.Controls.OfType<ControlPlayerParams>())
HomeTeam.Add(new Player(cpp.Param1, cpp.Param2, cpp.Param3));
BUT you should rethink the architecture of the app a bit if you ask me.