I have a tablelayout panel in which I am adding rows programmatically. When Adding rows to this container the first time the form is opened (using statement with new ValidationForm()) the table displays fine. Upon the second time, I get added rows at the top for some reason? See the picture here:
This does not affect the number of rows that I use, just displays extra lines. And the number of lines increases each time i close and open the form. Here is the code i use to add rows:
private void insertRow(Label label, dynamic control)
{
// Create Panel
var panel = new Panel();
// Set Object Properties
label.TextAlign = ContentAlignment.MiddleCenter;
label.Dock = DockStyle.Fill;
control.Dock = DockStyle.Fill;
panel.Dock = DockStyle.Fill;
//generate delete button
var delete = new Button();
delete.Text = "X";
delete.ForeColor = System.Drawing.Color.Red;
delete.MaximumSize.Height.Equals(40);
delete.Name = rowCount.ToString();
delete.Dock = DockStyle.Right;
delete.Click += new EventHandler(deleteRow);
// Add Controls to the panel
panel.Controls.Add(control);
panel.Controls.Add(delete);
// add controls
//tableLayoutPanel_Validations.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 48F));
tableLayoutPanel_Validations.RowCount = rowCount + 1;
tableLayoutPanel_Validations.Controls.Add(label, 0, rowCount);
tableLayoutPanel_Validations.Controls.Add(panel, 1, rowCount);
tableLayoutPanel_Validations.RowStyles.Add(new RowStyle(SizeType.AutoSize));
rowCount++;
}
rowCount is a private int that is set to 0 at runtime.
My first instinct is that the entire form would "reset" after the using statement. The form declaration is public partial class ValidationForm: Form {}
Using statement opening the form:
using (AVBuilder.ValidationForm validationBuilder = new AVBuilder.ValidationForm())
{
if (validationBuilder.ShowDialog() == DialogResult.OK)
{
// ADD ITEM TO Validations LIST
ListViewItem result = new ListViewItem(validationBuilder.resultObject + " : " + validationBuilder.resultName);
result.SubItems.Add(validationBuilder.resultJson);
result.ToolTipText = result.Text;
listView_Validations.Items.Add(result);
//MessageBox.Show(validationBuilder.resultJson);
}
}
Make sure the rowCount variable is not declared static, as it will not reset to 0 on a new form instance.
Related
In my C# WinForm application I have a tableLayoutPanel with only one row and two columns. The left column contains a TreeView, the right one another tableLayoutPanel which was set to Dock: Fill in the designer. I want to create several GroupBoxes programmatically that should use the full width of this second column also when the form is resized.
In the designer of Visual Studio it looks like this:
But when the application runs, the GroupBoxes don't use the complete width and it looks like this:
I'm using two methods to create the GroupBoxes (you can ignore the int values):
private void addDashboardRow(int i, int s)
{
// Add one row to dashboardTableLayoutPanel
sensorTableLayoutPanel.RowCount++;
// Maybe this one is wrong, but SizeType.AutoSize doesn't work either
sensorTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 75f));
sensorTableLayoutPanel.Controls.Add(createSensorGroupbox(i, s));
}
private GroupBox createSensorGroupbox(int i, int sen)
{
GroupBox g = new GroupBox();
//g.Width = 500; // I don't want fixed sizes, height of 75px is sufficient
//g.Height = 75;
g.Dock = DockStyle.Fill;
g.Text = "Sensor " + sen.ToString();
// just one label as example...
Label temp = new Label();
temp.Text = "Temperature: ";
temp.Location = new Point(5, 20);
Label tempVal = new Label();
tempVal.Text = "°C";
tempVal.Location = new Point(5, 50);
tempVal.Name = "Sensor" + sen.ToString() + "_temp";
//[... more labels]
g.Controls.Add(temp);
g.Controls.Add(tempVal);
return g;
}
My questions are:
How can I fit the GroupBox into the complete width of the parent container?
How can I delete all rows? E.g. when looking up the sensors during run time all GroupBoxes are dublicated, but everything should be created newly.
Layout of the question: how should I format terms like TableLayoutPanel correctly in this forum? It's my second question here and I haven't found out, yet.
For the second question. Currently I'm using this method that doesn't do what I'm expecting:
private void clearDashboardTable()
{
if (sensorTableLayoutPanel.Controls.Count == sensorList.Count) {
//MessageBox.Show(sensorTableLayoutPanel.Controls.Count.ToString());
foreach (Control con in sensorTableLayoutPanel.Controls)
{
sensorTableLayoutPanel.Controls.Remove(con);
con.Dispose();
}
//sensorTableLayoutPanel.Dispose();
sensorList.Clear();
}
}
I commented out what I've tried so far in the above code or added additional comments. I used this topic to try to understand the basics of dynamic groupbox creation: Add Row Dynamically in TableLayoutPanel
One way to achieve the outcome you describe is to make a custom UserControl containing a docked GroupBox that contains a docked TableLayoutPanel.
To adjust the width when the container changes, attach to the SizeChanged event of the parent.
public partial class CustomGroupBox : UserControl
{
public CustomGroupBox() => InitializeComponent();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if((Parent != null) &&!DesignMode)
{
Parent.SizeChanged += onParentSizeChanged;
}
}
private void onParentSizeChanged(object? sender, EventArgs e)
{
if(sender is FlowLayoutPanel flowLayoutPanel)
{
Debug.WriteLine($"{flowLayoutPanel.Width}");
Width =
flowLayoutPanel.Width -
SystemInformation.VerticalScrollBarWidth;
}
}
public new string Text
{
get=>groupBox.Text;
set=>groupBox.Text = value;
}
}
Main Form
The main form is laid out similar to your image except to substitute a FlowLayoutPanel on the right side.
Now test it out with this minimal code in the method that loads the Main Form:
public partial class MainForm : Form
{
int _id = 0;
public MainForm()
{
InitializeComponent();
flowLayoutPanel.AutoScroll= true;
for (int i = 0; i < 5; i++)
{
var customGroupBox = new CustomGroupBox
{
Text = $"Sensor {++_id}",
Width = flowLayoutPanel.Width - SystemInformation.VerticalScrollBarWidth,
Padding = new Padding(),
Margin = new Padding(),
};
flowLayoutPanel.Controls.Add(customGroupBox);
}
}
}
Clear
An example of clearing the sensors would be to add a button and set a handler in the same Load method:
buttonClear.Click += (sender, e) => flowLayoutPanel.Controls.Clear();
I am working on a homework c# winforms project and would like to add date and time in top right corner of my main form in a way that in first row I have a date written in one label, and on second row I have time written in second label.
Also I need that those stick in the top right corner if form is resized.
I don't know if it matters, but those label controls are inside panel which is top docked in form, and this panel already contains two controls that are docked left.
example of what I want
I've been playing with anchor and dock properties but I can't get it to work in a way I want.
private void GlavnaForma_Load(object sender, EventArgs e)
{
timerDateTime.Start();
lblDate.Text = DateTime.Now.ToString("dddd, dd.M.yyyy", new CultureInfo("hr-HR"));
lblTime.Text = DateTime.Now.ToString("HH:mm:ss", new CultureInfo("hr-HR"));
}
private void timerDateTime_Tick(object sender, EventArgs e)
{
lblDate.Text = DateTime.Now.ToString("dddd, dd.M.yyyy", new CultureInfo("hr-HR"));
lblTime.Text = DateTime.Now.ToString("HH:mm:ss", new CultureInfo("hr-HR"));
}
Set the anchor to Top, Right like so:
There are several ways to do this.
I would probably make the main form have a table layout panel with one column and three rows. Make the top two rows be absolutely sized and the third row have size type "percent" with a value of 100.0% to take up all remaining room. Then put a label each in the top two rows and justify the labels to the right via setting their "Dock" property to "Right".
All of this can be done in the form designer GUI. The generated code looks like the following:
this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
this.labelDate = new System.Windows.Forms.Label();
this.labelTime = new System.Windows.Forms.Label();
this.tableLayout.SuspendLayout();
this.SuspendLayout();
//
// tableLayout
//
this.tableLayout.ColumnCount = 1;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Controls.Add(this.labelDate, 0, 0);
this.tableLayout.Controls.Add(this.labelTime, 0, 1);
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayout.Location = new System.Drawing.Point(0, 0);
this.tableLayout.Name = "tableLayout";
this.tableLayout.RowCount = 3;
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Size = new System.Drawing.Size(800, 450);
this.tableLayout.TabIndex = 0;
//
// labelDate
//
this.labelDate.AutoSize = true;
this.labelDate.Dock = System.Windows.Forms.DockStyle.Right;
this.labelDate.Location = new System.Drawing.Point(742, 0);
this.labelDate.Name = "labelDate";
this.labelDate.Size = new System.Drawing.Size(55, 24);
this.labelDate.TabIndex = 0;
this.labelDate.Text = "26-8-2019";
//
// labelTime
//
this.labelTime.AutoSize = true;
this.labelTime.Dock = System.Windows.Forms.DockStyle.Right;
this.labelTime.Location = new System.Drawing.Point(748, 24);
this.labelTime.Name = "labelTime";
this.labelTime.Size = new System.Drawing.Size(49, 24);
this.labelTime.TabIndex = 1;
this.labelTime.Text = "19:59:58";
Add whatever further content you want to the third row. Maybe add a panel to that row docked to "Fill"
I want to be able to do something like this so that i can just add label then label1 label 2 label3 etc and i cant create an array of new labels or anything since my labels have an image attached to them and have to keep a certain position as well. So making a new label that has some text as a label that wont show up anywhere in particular on my form isn't much use.
List<Label> labels = new List<Label>();
for (int i = 0; i < 10; i++)
{
labels.Add(label + (i.ToString()));
}
You can add Labels to list from Form by name like this :
List<Label> labels = new List<Label>();
for (int i = 0; i < 10; i++)
{
var label =
this.Controls.OfType<Label>().Where(lb => lb.Name == "label" + i).FirstOrDefault();
if (label != null)
labels.Add(label);
}
Here is a Code Sample:
You need to add the labels to the Controls collection of the tab page:
Page2.Controls.Add(CustomLabel[CustomLabel.Count - 1] as Control);
List<Label> customLabels = new List<Label>();
foreach (string ValutaCustomScelta in Properties.Settings.Default.ValuteCustom)
{
Label label = new Label();
label.Location = new System.Drawing.Point(317, 119 + customLabels.Count*26);
label.Parent = Page2;
label.Name = "label" + ValutaCustomScelta;
label.Text = ValutaCustomScelta;
label.Size = new System.Drawing.Size(77, 21);
customLabels.Add(label);
Page2.Controls.Add(label);
}
Have you added the labels by code or by design?
If you added them by code just add them then to the list.
If you added them by design there are two options. They are all on the same container (panel, form, groupbox...) or they are not.
If they are you can:
for each(control ctr in $yourcontainer.controls)
{
if(ctr is Label)
{
labels.add(ctr);
}
}
If they are not in the same container you can add them by hardcode
labels.add(%label1name);
labels.add(%label2name);
labels.add(%label3name);
labels.add(%label4name);
But if they are too much, you can run the code before for each one of the panels
I've a TableLayoutPanel that has 10 rows and 2 columns.
My problem is, when I add a label in the first column and a button in the other, everything is fine. But when I add another label and a button on e.g. the next row, the previous rows are empty. It seems that my program only shows to last added row.
Code:
Label lblProjectName = new Label();
lblProjectName.Text = "test";
lblProjectName.Anchor = AnchorStyles.Left;
Button btnProject = new Button();
btnProject.Text = "Fill In";
tlpProject.Controls.Add(lblProjectName, 0, 0);
tlpProject.Controls.Add(btnProject, 1, 0);
tlpProject.Controls.Add(lblProjectName, 0, 1);
tlpProject.Controls.Add(btnProject, 1, 1);
Tnx!
You have to create a new control for each cell in the panel.
Currently, your code is moving the controls to the last column and last row.
Something like this:
for (int i = 0; i < numRows; ++i) {
Button btnProject = new Button();
btnProject.Text = "Fill In";
tlpProject.Controls.Add(btnProject, 1, i);
}
Any event handlers would have to be added here too, where on the handler end, you have to check the "sender" of the object to see which button the user pressed.
It seems like Label and Button can only be children to single control while you are adding them into different cells(thus different controls). If you need all cells filled with Labels and Buttons you will need 10 Labels and 10 Buttons for each column. Look at the code:
for(int i=0;i<10;i++){
var lblProjectName = new Lable();
lblProjectName.Text = "test";
lblProjectName.Anchor = AnchorStyles.Left;
var btnProject = new Button();
btnProject.Text = "Fill In";
tlpProject.Controls.Add(lblProjectName, 0, i);
tlpProject.Controls.Add(btnProject, 1, i);
}
I have a dataset that returns questions and answers from the database, each answer in answer table is linked via a forgein key to the question table.
What I would like to achive is the following:
That a a single dynamic form is created on the first question with all the questions listed under it, meaning the FK_table_Answers = PK_table_Questions on the table_Answers:
(eg) This is Form for Question One:
Question One: How old are you?
Answer One (this is a radio button)
Answer Two (this is a radio button)
Answer Three (this is a radio button)
I have managed to get the above kind of working but the problem is my code keeps generating the forms (eg) 3 times becuase there are three answers, so can anyone point me in right direction how to generate the form dynamically but only ONCE - meaning each successive form is created only once as per the question and that particular questions answers under it.
Kind regards
UPDATED code:
private void LoadDataSets()
{
if (_dataSetQuestionnaire.tbl_QuestionnaireQuestion.Rows.Count == 0)
{
try
{
//Questionnaire:
DataSet1TableAdapters.tbl_QuestionnaireTableAdapter questionnaireAdapter =
new DataSet1TableAdapters.tbl_QuestionnaireTableAdapter();
questionnaireAdapter.Fill(_dataSetQuestionnaire.tbl_Questionnaire);
//Category:
DataSet1TableAdapters.tbl_QuestionnaireCategoryTableAdapter categoryAdapter =
new DataSet1TableAdapters.tbl_QuestionnaireCategoryTableAdapter();
categoryAdapter.Fill(_dataSetQuestionnaire.tbl_QuestionnaireCategory);
//QuestionnaireQuestion:
DataSet1TableAdapters.tbl_QuestionnaireQuestionTableAdapter questionnaireQuestionAdapter =
new DataSet1TableAdapters.tbl_QuestionnaireQuestionTableAdapter();
questionnaireQuestionAdapter.Fill(_dataSetQuestionnaire.tbl_QuestionnaireQuestion);
//QuestionnaieAnswer:
DataSet1TableAdapters.tbl_QuestionnaireAnswerTableAdapter questionnaireAnswerAdapter =
new DataSet1TableAdapters.tbl_QuestionnaireAnswerTableAdapter();
questionnaireAnswerAdapter.Fill(_dataSetQuestionnaire.tbl_QuestionnaireAnswer);
using (DataSet1 dSet = new DataSet1())
{
//Questionnaire:
dSet.Merge(_dataSetQuestionnaire.tbl_Questionnaire);
//Category:
dSet.Merge(_dataSetQuestionnaire.tbl_QuestionnaireCategory);
//QuestionnaireQuestion:
dSet.Merge(_dataSetQuestionnaire.tbl_QuestionnaireQuestion);
//QuestionnaieAnswer:
dSet.Merge(_dataSetQuestionnaire.tbl_QuestionnaireAnswer);
int primaryKeyQuestionnaire = Convert.ToInt32(_dataSetQuestionnaire.tbl_Questionnaire.Rows[0][0]);
foreach (DataSet1.tbl_QuestionnaireRow questionnaire
in _dataSetQuestionnaire.Tables["tbl_Questionnaire"].Select(String.Format("pk_tbl_Questionnaire = {0}", primaryKeyQuestionnaire)))
{
foreach (DataSet1.tbl_QuestionnaireCategoryRow category
in _dataSetQuestionnaire.Tables["tbl_QuestionnaireCategory"].Select(String.Format("fk_tbl_Questionnaire = {0}", questionnaire.pk_tbl_Questionnaire)))
{
foreach (DataSet1.tbl_QuestionnaireQuestionRow question
in _dataSetQuestionnaire.Tables["tbl_QuestionnaireQuestion"].Select(String.Format("fk_tbl_QuestionnaireCategory = {0}", category.pk_tbl_QuestionnaireCategory)))
{
int radiobuttonPosition = 0;
foreach (DataSet1.tbl_QuestionnaireAnswerRow answer
in _dataSetQuestionnaire.Tables["tbl_QuestionnaireAnswer"].Select(String.Format("fk_tbl_QuestionnaireQuestion = {0}", question.pk_tbl_QuestionnaireQuestion)))
{
//Gets the questins via the FK_questionnaireQuestion and fill the _dataSetRadioButtons to generate on dynamic form.
DataSet1TableAdapters.tbl_QuestionnaireAnswerTableAdapter a =
new DataSet1TableAdapters.tbl_QuestionnaireAnswerTableAdapter();
DataSet dSetRadioButtons = new DataSet();
dSetRadioButtons.Merge(a.GetDataByQuestionnaireQuestion(answer.fk_tbl_QuestionnaireQuestion));
_dataSetRadioButtons = dSetRadioButtons;
string theQuestion = question.tbl_QuestionnaireQuestion_Description.ToString();
Form form = new Form();
_form = form;
if(_form == null)
{
_form = new Form();
}
else
{
form.Height = 400;
form.Width = 550;
form.StartPosition = FormStartPosition.CenterScreen;
Label label = new Label();
label.Location = new System.Drawing.Point(5, 10);
label.Size = new System.Drawing.Size(450, 25);
label.Text = theQuestion;
Panel panel = new Panel();
panel.Size = new System.Drawing.Size(300, 200);
panel.Location = new System.Drawing.Point(15, 50);
panel.BackColor = Color.Yellow;
System.Windows.Forms.RadioButton[] radioButtons = new System.Windows.Forms.RadioButton[_dataSetRadioButtons.Tables["tbl_QuestionnaireAnswer"].Rows.Count];
for (int i = 0; i < _dataSetRadioButtons.Tables["tbl_QuestionnaireAnswer"].Rows.Count; i++)
{
radioButtons[i] = new RadioButton();
radioButtons[i].Text = _dataSetRadioButtons.Tables["tbl_QuestionnaireAnswer"].Rows[i]["tbl_QuestionnaireAnswer_Description"].ToString();
radioButtons[i].Location = new System.Drawing.Point(60, 20 + i * 20);
//panel.Controls.Add(radioButtons[i]);
radioButtons[i].Click += new EventHandler(Form1_Click);
Int64 item = Convert.ToInt64(_dataSetRadioButtons.Tables["tbl_QuestionnaireAnswer"].Rows[0].ItemArray[3].ToString());
panel.Controls.Add(radioButtons[i]);
}
Button nextButton = new Button();
nextButton.Text = "Next";
nextButton.Name = "button";
nextButton.Location = new System.Drawing.Point(200, 300);
nextButton.Size = new System.Drawing.Size(150, 25);
nextButton.Click += new EventHandler(nextButton_Click);
form.Controls.AddRange(new Control[] { panel, label, nextButton });
form.ShowDialog();
CreateBoxAndQuestion(form, panel, label);
//form.Dispose();
}
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
{
MessageBox.Show("Error");
}
}
private void CreateBoxAndQuestion(Form f, Panel p, Label l)
{
for (int i = p.Controls.Count - 1; i >= 0; i--)
{
p.Controls.RemoveAt(i);
}
}
If I understand correctly, you call CreateBoxAndQuestion every time a new question is selected.
But you create (and destroy) the form each time your code enters in this method.
A first approach would be to keep the form, label, button and panel creation outside the method (perhaps in design mode) then passing these controls to the method as
EDIT: Somewhere in your code before entering the load of your next question:
frmQuestion _form = null; // Global
// Create a global instance and keep it without displaying
if(_form == null) _form = new frmQuestion(); // frmQuestion created with panel, label e button via FormDesigner
then when you need to populate _form call
CreateBoxAndQuestion(_form, _form.Panel, _form.Label, questionText);
In this example I pass the _form to CreateBoxAndQuestion, but this is not necessary because is a global. You could change CreateBoxAndQuestion to use directly the global instance.
private void CreateBoxAndQuestion(frmQuestion f, Panel p, Label l, string _label)
{
// Do not display your form here....
}
Now when entering the method clear every RadioButton controls in the panel.Controls collection
for (int i = p.Controls.Count - 1; i >= 0; i--)
{
p.Controls.RemoveAt(i);
}
the rest of the code should change only to reflect the new text assigned to the label and controls and the re-adding of the RadioButtons to the panel. No more creation and reinitialization for Form Label, Button.
label.Text = _label;
Don't forget to destroy _form at the end of your program with _form.Dispose();