I have code that creates a button for each object in a list. When each object is created it is given a name that corresponds to the row and column (i.e. name = row1col2). Each button is generated dynamically, added to the grid and then the row/column are set. At a later time I need to gather the "selected" button data so I can perform actions on the data they represent. When I attempt to get the control data from the buttons everything is fine, except for the grid row/column data. It remains the same for all of the selected rows and columns in a grid.
Creating buttons:
for (int i = 1; i < row.StackCount+1; i++)
{
//create button for the column
stackButton = new Button();
stackButton.Height = ((newRow.Height - 2));
stackButton.Width = ((newRow.Width / row.StackCount) - 2);
stackButton.Background = new SolidColorBrush(Colors.White);
//add the button border
stackButton.BorderBrush = new SolidColorBrush(Colors.Black);
stackButton.BorderThickness = new Thickness(1);
stackButton.Style = Application.Current.Resources["flatButton"] as Style;
//add the button name
stackButton.Name = "Row" + row.LineNumber + "Col" + (i - 1).ToString();
//add the event handler to the button
stackButton.Click += new RoutedEventHandler(stackButton_Click);
//add a new column
newRow.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(newRow.Width, GridUnitType.Star) });
//put the button into the grid
newRow.Children.Add(stackButton);
Grid.SetRow(stackButton, 0);
Grid.SetColumn(stackButton, i-1);
}
Getting the Button data back
g = (Grid)b.Child;
foreach (Button currentButton in g.Children)
{
if (((SolidColorBrush)currentButton.Background).Color == Colors.Gray)
{
//create a stack object
buttonData.StartDate = DateTime.Now;
buttonData.LotNumber = LotDisplay.Text;
buttonData.RoomID = SilverGlobals.CurrentRoom.RoomID;
buttonData.RoomCol = Grid.GetColumn(currentButton);
buttonData.RoomRow = Grid.GetRow(currentButton);
buttonData.TrayCount = int.Parse(currentButton.Content.ToString());
buttonData.Status = 0;
//add stack object to list of stack objects
stacks.Add(buttonData);
}
}
I know this must be something small I am missing. Anyone got any ideas?
Although the comment in your second section of code says:
//create a stack object
you don't actually create a new stack object so it is simply overwriting the single instance of buttonData on each iteration. The values for row and column that you see at the end are the last iteration's values.
The net effect is that stacks is a collection of all the same instance of an object instead of a collection of separate instances.
This is just a shot in the dark, but based on this question and this question, it might be that you need to set the Row and Column properties before you add the button to its parent - something like this:
//put the button into the grid
Grid.SetRow(stackButton, 0);
Grid.SetColumn(stackButton, i-1);
newRow.Children.Add(stackButton);
Related
I have a TableLayoutPanel on a form which has 8 columns and 8 rows. I'm trying to populate this table from a list of a user controls which each contain three labels. When I run the app, I can see it has been populated, but looking at the actual form, it is empty.
I've tried replacing the control with just a label, and they show up fine, it's just when I try to use the user control that it doesn't work.
This code runs in the Load method and generates a list of GridSquareModel, which is used to populate the user control. Each model has an X and Y position which relates to their cell on the table panel.
var startup = new StartupForm();
if (startup.ShowDialog() == DialogResult.OK)
{
var grid = new List<GridSquareModel>();
for (int i = 0; i < 64; i++)
{
Position pos = new Position();
pos.X = i % 8;
pos.Y = i / 8;
grid.Add(new GridSquareModel(pos));
}
RenderGrid(grid);
}
else
{
Application.Exit();
}
This method generates a GridSquareControl for every GridSquareModel in the list. The X and Y value in the position determines which cell they are added to.
private void RenderGrid(List<GridSquareModel> grid)
{
UIGrid.Clear();
GridTable.Controls.Clear();
foreach (var item in grid)
{
var control = new GridSquareControl(item)
{
Dock = DockStyle.Fill
};
GridTable.Controls.Add(control, item.Position.X, item.Position.Y);
//GridTable.Controls.Add(new Label
//{
// Text = "TEST"
//}, item.Position.X + 1, item.Position.Y + 1);
}
}
I have commented my attempt to replace the GridSquareModel with a Label, which worked. This should produce a table containing 64 controls but once it is loaded the table appears empty.
I'm an idiot. I created a constructor for GridSquareControl but it did not include the InitializeComponent method, so obviously it was not rendered. All is working as expected now.
I am creating asp.net web form. in that i am creating dynamic tables in which particular column is numeric text box control.
i don't know how to assign and get values from the text box control.. my coding as follow..
for (int i = 0; i < my_DataTable.Rows.Count; i++)
{
HtmlTableRow _Row = new HtmlTableRow();
HtmlTableCell Col = new HtmlTableCell();
Col.InnerText = my_DataTable.Rows[i]["itmCode"].ToString();
_Row.Controls.Add(Col);
Col = new HtmlTableCell();
_Row.Controls.Add(Col);
Col.InnerHtml = "<input type='number' value='0'>";
_Row.Controls.Add(Col);
my_Table.Rows.Add(_Row);
}
In a paricular method, i need to assign the value to the text box control also needs to get the value existing value.. so i try follow as below
var no_1 = my_Table.Rows[0].Cells[1].InnerText;
If i check the no_1, it has the textbox, but i don't know how to access the current value and assign new value..
can anyone help me how to achieve this..
One thing you have to keep in mind while working with Dynamic Controls is that whenever a postback has occurred you will lose the dynamically created controls(as the postback calls the Page_load() event so if you don't have them at the load event they will not be generated and hence will not be displayed.). So, it is always better to re-render the controls in the load event.
So, in order to get the value of the dynamically assigned controls (either HTML or Asp.net) here is how i would do that.
First, create a holder which will be used to store the controls in the page either with runat="server"(So, you can access that control in the backend). In your case, that control is my_Table. Then use the Session/ViewState to keep a track of all the created dynamic controls which can be used re-render the controls with their values as:
To add a new control in the page it would be like this:
var cnt = _findRelated("txtDynamic") + 1; //for all the dynamic text boxes i am using the prefix of txtDynamic just to keep SOC.
var nId = $"txtDynamic-{cnt}";
var _ctrl = new HtmlInputText("Integer")
{
Name = nId,
ID = nId,
//Value="Default Value" //uncomment to assign a default value
};
_ctrl.Attributes.Add("runat", "server");
var row = new System.Web.UI.HtmlControls.HtmlTableRow();
var newCell = new HtmlTableCell();
newCell.Controls.Add(_ctrl);
row.Cells.Add(newCell);
my_Table.Rows.Add(row);
Session.Add(cnt.ToString(), _ctrl); //here i am using session to manage the controls but you can also use the ViewState
In the above code i am using HtmlInputText to generate an <input type="number"></input> with it's constructor taking the type string more can be read at:HtmlInputText.
The _findRelated() method is used to get the number of dynamic text controls appended to the Form. It is defined as:
private int _findRelated(string prefix)
{
string reqstr = Request.Form.ToString();
return ((reqstr.Length - reqstr.Replace(prefix, "").Length) / prefix.Length);
}
To set the value of the dynamically added control we can do something like this(if not assigned at the creation):
var cell = my_Table.Rows[_myTable.Rows.Count-1].cells[0]; //here i have assumed it is in the last row and in the first cell you can change the index to be anything.
var txtDynamic = cell.Controls.OfType<HtmlInputText>().FirstOrDefault();//getting the control
txtDynamic.Value = "<Some thing new>"; //setting the value
Now, to get the assigned the value:
var cell = my_Table.Rows[_myTable.Rows.Count-1].cells[0]; //here i have assumed it is in the last row and in the first cell you can change the index to be anything.
var txtDynamic = cell.Controls.OfType<HtmlInputText>().FirstOrDefault();//getting the control
//now use the .Value property of the control to get the value as:
var nValue = txtDynamic.Value;
And as we know the dynamically added controls will be lost on the postback event then we can create a method which will use the controls stored in the Session and re-render them with their values as:
private void _renderControls()
{
try
{
if (Session.Count > 0)
{
for (int k = 0; k < Session.Count; k++)
{
if (Session[k] != null)
{
var _ctrl = new HtmlInputText("Integer") //you can make it dynamic to add different types of input control
{
Name = ((HtmlInputText)Session[k]).ID,
ID = ((HtmlInputText)Session[k]).ID,
Value = ((HtmlInputText)Session[k]).Value
};
if (_ctrl != null)
{
_ctrl.Attributes.Add("runat", "server");
var row = new System.Web.UI.HtmlControls.HtmlTableRow();
var newCell = new HtmlTableCell();
newCell.Controls.Add(_ctrl);
row.Cells.Add(newCell);
my_Table.Rows.Add(row);
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Now, let's modify the Page_load() event to call this method on every postback as:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
_renderDynamic(); // this method will be called if there is an postback event to re-render the dynamic controls
}
}
Note:
This is just a sample(there can be a lot better approaches out there).
I have used HtmlInputText with property as Integer to create ainput[type="number"].
I am trying to load multiple DataTables into DataGridViews in separate tabpages of a TabControl, the DataTables are stored in a Dictionary which is in turn stored in a DataObject class.
I can get everything displaying correctly but when I try to add loop through the Datagridview Header to add a contextmenustrip, for some reason I can get it to work for the first DataGridView but subsequent DataGridViews do not have the context menu applied? I have tried adding a print statentment just before the foreach loop and the DataGridView gets a column count of zero…so im guessing that’s why the foreach loop isn’t doing anything…but all the data is still displayed correctly in the dataGridViews in their respective tabs…any help in pointing out what I’m missing would be greatly appreciated.
Regards
Amarino
Code given below
List<ImportObject> lImportObjects = new List<ImportObject>();
private void loadImportFilesToScreen(List<ImportObject> lImportObjects)
{
foreach (ImportObject lImportObject in lImportObjects) {
DisplayImportFiles(lImportObject);
}
}
public void DisplayImportFiles(ImportObject pImportObject)
{
string lTabName="";
//load DataGridView with DataTable
/*
foreach (KeyValuePair<string, DataTable> lDT in pImportObject.DataTableDictionary)
{
lTabName = DisplayTabsInApp(pImportObject.FileName + "_" + lDT.Key, lDT.Key);
LoadDatatableIntoGrid(lDT.Value, lTabName);
}
*/
for (int i = 0; i < pImportObject.DataTableDictionary.Count; i++)
{
KeyValuePair<string, DataTable> lItem = pImportObject.DataTableDictionary.ElementAt(i);
string lKey = lItem.Key;
DataTable lDT = lItem.Value;
lTabName = DisplayTabs(pImportObject.FileName + "_" + lKey, lKey);
LoadDatatableIntoGrid(lDT, lTabName);
lDT = null;
}
}
public string DisplayTabs(string pTabName, string pSheetName)
{
// Create a new Tab Page for this file. Set heading, set name.
TabPage lTabPage_NewFile = new TabPage();
lTabPage_NewFile.Text = pTabName;
lTabPage_NewFile.Name = "TAB_PAGE_" + pTabName;
tabControl_ImportFiles.TabPages.Add(lTabPage_NewFile);
return lTabPage_NewFile.Name;
}
public void LoadDatatableIntoGrid(DataTable pDataTable, string pTabName) {
DataGridView lDGV = new DataGridView();
lDGV.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing;
lDGV.RowHeadersVisible = false;
BindingSource BindingSource1 = new BindingSource(); //create new data binding source
BindingSource1.DataSource = pDataTable; //SetData source
lDGV.DataSource = BindingSource1;
lDGV.RowHeadersVisible = true;
tabControl_ImportFiles.TabPages[pTabName].Controls.Add(lDGV);
//DataGridView lDGV = tabControl_ImportFiles.TabPages[pTabName].Controls[0] as DataGridView;
PrintToConsoleInARD("DataGridView Column Count: " + lDGV.Columns.Count.ToString());
bool runOnce = true;
foreach (DataGridViewColumn lDGVColumn in lDGV.Columns) {
lDGVColumn.HeaderCell.ContextMenuStrip = lCMS_ColumnHeaders;
}
lDGV.Dock = DockStyle.Fill;
lDGV.VirtualMode = true;
BindingSource1 = null;
lDGV = null;
}
I am confident the reason for this is because the “tab page” is not displayed. As the trace is described inside the LoadDatatableIntoGrid method… if you put a breakpoint at the line…
tabControl_ImportFiles.TabPages[pTabName].Controls.Add(lDGV);
Add a watch to the variable lDGV.Columns.Count… will show that IDGV has zero (0) columns. Execute the line above and magically, the grid IDGV has columns. This appears correct since this is the first “tab page” and it is the active (displayed) tab page. Point being… if you do not add the grid to a “Active/Shown/Displayed” tab page most UI code will be ignored. This is why the next time around; the grid will be empty because the grid is added to a non “active/shown/displayed” tab page.
A simple solution is to simply “Show” the tab page before you add the grid to it. This appears to fix the issue you describe. Add the line below before the line above…
tabControl_ImportFiles.TabPages[pTabName].Show();
Hope this helps.
I'm auto generating checkboxes and putting them into a panel,but when I do that, they all appear at the same point. I can manually move them apart, but this could cause problems down the line. Is there a way to get them to automatically align under each other.
Here's my code:
foreach (Category i in DesktopApp.getBaseCat())
{
checkBox = new CatBox();
checkBox.setCat(i);
checkBox.Text = i.ToString(); // puts the name of the category in the text field
checkBox.Click += new EventHandler(simpleCatBox_Click);
this.categoryPanel.Controls.Add(checkBox); // adds it to the panel
step++; // increments step
}
Give the checkbox a margin (10px in this example)
checkBox.Margin = new Thinkness(10);
Or, if you want different margins
checkBox.Margin = new Thickness(left,top,right,bottom);
I have a loop that is supposed to go through DataTable and for each row create a new GroupBox, set it's text to value from one column, in that GroupBox I want to put a Label with Text similar to another column in the table.
This is just part of the code!
for (int i = 0; i < tab.Rows.Count; i++)
{
lblbox[i] = new GroupBox();
lblbox[i].Text = tab.Rows[i]["text"].ToString();
lblbox[i].Name = "box no " + i.ToString();
lblbox[i].Visible = true;
this.Controls.Add(lblbox[i]);
lblbox[i].Location = new Point(5, 55 * i);
lblbox[i].Height = 50;
lblbox[i].SendToBack();
importancelbl[i] = new Label();
importancelbl[i].Text = "Importance: " + tab.Rows[i]["importance"].ToString();
importancelbl[i].Name = "implbl" + i.ToString();
importancelbl[i].Visible = true;
lblbox[i].Controls.Add(importancelbl[i]);
importancelbl[i].BringToFront();
Point locP = new Point();
locP.X = lblbox[i].Location.X + 5;
locP.Y = lblbox[i].Location.Y + 15;
importancelbl[i].Location = locP;
}
When i run the code it creates three (I have three rows in my table) GroupBoxes correctly and creates all the labels, but only first label is visible in its Groupbox. When I add those labels to the Form and not to the GroupBox, all of them are visible, but I want them to be in boxes...
I've tried pretty much everything and I'm still very confused (espacially by the behavior of the first label). I know the mistake is probably obvious and stupid, but I just can't find it!
Control.Location is relative to its parent, so set Location for the label to (5, 15).
locP.X = 5;
locP.Y = 15;
My guess is that they are somehow overlapping and making each other disappear somehow.
Could you try posting pictures of the form when it works and when it doesn't? Also add all your code?
Try to preform adding
lblbox[i].Controls.Add(importancelbl[i]);
this.Controls.Add(lblbox[i]);
after setting all your properties