NullReferenceException dynamically created controls, object seems to exist - c#

In Win Forms I have these three test methods. First to create a button, second to create a tab control with two tabs and third to move created button to first tab.
private void button1_Click(object sender, EventArgs e)
{
Button przycisk = new Button();
przycisk.Location = new Point(24, 250);
przycisk.Name = "nowy";
przycisk.Text = "utworzony";
przycisk.Width = 131;
przycisk.Height = 23;
Controls.Add(przycisk);
}
private void button2_Click(object sender, EventArgs e)
{
TabControl zakladki = new TabControl();
zakladki.Location = new Point(208, 160);
zakladki.Name = "zakl";
zakladki.Height = 150;
zakladki.Width = 208;
zakladki.TabPages.Add("zakladka1", "pierwsza");
zakladki.TabPages.Add("zakladka2", "druga");
Controls.Add(zakladki);
}
private void button3_Click(object sender, EventArgs e)
{
TabControl zakladki = (TabControl)Controls.Find("zakl", false).FirstOrDefault();
int numerZakladki = 1;
foreach (TabPage zakladka in zakladki.TabPages)
{
Control kt = Controls["nowy"];
kt.Location = new Point(10, 10); // System.NullReferenceException
zakladka.Controls.Add(kt);
numerZakladki++;
}
}
I'm having a hard time to understand the behavior upon trying to change the referenced button location. The code above throws System.NullReferenceException, but when I do
if (kt != null)
{
kt.Location = new Point(10, 10);
}
it works as expected. Can anyone explain it to me ?

The new TabControl contains two Tabs.
If you move the button to the first Tab, the control at the Main form is null.
Code without Loop:
private void button3_Click(object sender, EventArgs e)
{
TabControl zakladki = (TabControl)Controls.Find("zakl", false).FirstOrDefault();
Control kt = Controls["nowy"];
kt.Location = new Point(10, 10);
zakladki.TabPages[0].Controls.Add(kt);
}

Your nowy controls are added to the root control (Form I guess), but you are trying to find them in the tab pages under zakl. You have to either add the controls to the tab page, or find them in the root control, just like you did with zakl.

You move the "nowy" button from the Form.Controls to the first TabPage's Controls collection. This removes the control from the first collection, so the code throws the exception on the next iteration. A Control can have only a singleParent.
Either create the Button for each tab separately (instead of moving it), or add the Button to the first tab only (without the foreach loop).

Related

C# WinForms contextmenu focus issue when textbox is added

In a C# WinForms application I need to create a ContextMenuStrip with dropdown and textbox:
private System.Windows.Forms.ContextMenuStrip ct1;
private void button_Click(object sender, EventArgs e)
{
var header = new ToolStripMenuItem("Header");
header.Enabled = false;
var options = new ToolStripMenuItem("Options");
for (int i = 0; i < 5; i++)
{
var checkoption = new ToolStripMenuItem("Check Me " + i + "!");
checkoption.CheckOnClick = true;
options.DropDownItems.Add(checkoption);
}
var txt = new ToolStripTextBox();
txt.Text = "changeme";
options.DropDownItems.Add(txt);
options.DropDown.Closing += DropDown_Closing;
ct1.Items.Clear();
ct1.Items.Add(header);
ct1.Items.Add(options);
ct1.Show(this, button.Left, button.Top);
}
private void DropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e)
{
e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked);
}
Now, e.Cancel will prevent closing the dropdown if the reason is ItemClicked, so I can select more items without having to open the menu again:
Please note that "changeme" is a ToolStripTextBox!
Once I focus it (click on it), I can edit the text inside:
After finish editing the textbox, I still can change the checkbox items, but there is no focus indicator:
How can I get back the focus indicator just as shown on the first picure?
Note: if I move the mouse onto "Header", the dropdown will close, and then moving it back to "Options", will reopen the dropdown and then the focus indicator is good again:
How can I do this without closing and reopening the dropdown?
I have tried Select() for the options item, but it did not help, neither Invalidate() on ct1.
Just have found it:
First needs to add a click handler on the dropdown:
options.DropDown.Click += DropDown_Click;
Then in the click handler it needs to be focused:
private void DropDown_Click(object sender, EventArgs e)
{
var dropdown = (ToolStripDropDown)sender;
dropdown.Focus();
}

Multiple message box showing in c#

I am making a one form application and created a hard coded class for my GUI and event handlers.
At first the messagebox is showing once but when I click another button it is showing multiple times, increasing every click of a button.
I have not yet created a unhandled exception is that something to do with that? And if it is unhandled exception how can I do it?
private void btnInventoryAddFanbelt_click(object sender, EventArgs e)
{
// CONTENTS
// labels
lblFanbeltName.Name = "lblFanbeltName";
lblFanbeltName.Text = "Fanbelt name";
lblFanbeltName.Visible = true;
lblFanbeltName.Location = new Point(10, 10);
// textbox
txtFanbeltName.Name = "txtFanbeltName";
txtFanbeltName.Visible = true;
txtFanbeltName.Location = new Point(115, 8);
// buttons
btnInventorySaveFanbelt.Name = "btnInventorySaveFanbelt";
btnInventorySaveFanbelt.Text = "Add fanbelt";
btnInventorySaveFanbelt.Visible = true;
btnInventorySaveFanbelt.Location = new Point(125, 50);
// END OF CONTENTS
// REMOVE CONTROLS
split3.Panel2.Controls.Clear();
// ADD CONTROLS
split3.Panel2.Controls.Add(lblFanbeltName);
split3.Panel2.Controls.Add(txtFanbeltName);
split3.Panel2.Controls.Add(btnInventorySaveFanbelt);
// EVENTHANDLERS
btnInventorySaveFanbelt.Click += new EventHandler(btnInventorySaveFanbelt_click);
}
private void btnInventorySaveFanbelt_click(object sender, EventArgs e)
{
MessageBox.Show("Fanbelt added");
}
Every time you click on btnInventoryAddFanbelt, you are adding another click event:
btnInventorySaveFanbelt.Click += new EventHandler(btnInventorySaveFanbelt_click);
Only add it once, usually best to do that in the constructor or through the designer. It can also be shortened to just:
btnInventorySaveFanbelt.Click += btnInventorySaveFanbelt_click;
Also, split3.Panel2.Controls.Clear(); does not dispose of the controls, it just removes them, so you are leaking memory here. Try using:
while (split3.Panel2.Controls.Count > 0) {
split3.Panel2.Controls[0].Dispose();
}

Datagridview shall disappear when clicking on the Form in the background

As described in the title, I have a Form with a Datagridview on the front. The datagridview is smaller than my form in the back and I want the Datagridview to disappear whenever I click anywhere else but the Datagridview.
My code looks like this:
this.dataGridView1.Leave += new System.EventHandler(this.focus);
and the Eventhandler is defined like this:
private void focus(object sender, EventArgs e)
{
if(dataGridView1.Focused == false)
{
dataGridView1.Visible = false;
}
}
My problem is that my Datagridview only disappears when a new event in my form is activated but not when I click for example in a textbox on my form.
Can anyone help me?
The Leave event will not raise if you click on Form, or a ToolStripButton, PictureBox or any other non-selectable control.
If you expect a behavior like a dropdown, you can host DataGridView in a ToolStripControlHost and show it using a ToolStripDropDown. This way when you click anywhere outside the `DataGridView, it will disappear. It will act like a dropdown menu. Also the grid can be larger than your form:
private void button1_Click(object sender, EventArgs e)
{
this.dataGridView1.Margin = new Padding(0);
var host = new ToolStripControlHost(this.dataGridView1);
this.dataGridView1.MinimumSize = new Size(200, 100);
host.Padding = new Padding(0);
var dropdown = new ToolStripDropDown();
dropdown.Padding = new Padding(0);
dropdown.Items.Add(host);
dropdown.Show(button1, 0,button1.Height);
}
Important Note: It's an example. It's better to pay attention to disposing of objects in a real world application. For example, use just a single ToolStripdropDown and dispose it when closing the form.
Change the event handler assigning to:
this.dataGridView1.Leave += new System.EventHandler(fokussiert);
Worked for me when focusing on a textbox
you want your dgv also to disapear when you click on your textbox? is that what you mean?
private void dataGridView1_Leave(object sender, EventArgs e)
{
dataGridView1.Visible = false;
}
private void Form1_Click(object sender, EventArgs e)
{
dataGridView1.Visible = false;
}

DataGridView Right Click Specific Column for ContextMenuStrip

With C# I am trying to only show a ContextMenuStrip (CMS) when I right click a specific column in my DataGridView. I am confused as to whether I should be using a DataGridView_CellContentClick and/or dataGridView1.HitTest(). Then to finish off my problem I want to send the data from that right clicked cell to a new form window.
My current code has strange behavior. It will not show the CMS unless I have first left click or right clicked the 4th column. However once I have it will always show the CMS on a right click.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 4)
{
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
//Assign event handlers
MenuOpenPO.MouseUp += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the Datagrid
dataGridView1.ContextMenuStrip = Menu;
}
}
void MenuOpenPO_Click(object sender, MouseEventArgs e)
{
var ht = dataGridView1.HitTest(e.X, e.Y);
MessageBox.Show("Hello2");
PO_Form POWindow = new PO_Form();
POWindow.Show();
}
I was going to use the var ht = dataGridView1.HitTest(e.X, e.Y); to grab the cell value.
Any help would be appreciated, thanks!
Edit 1
So I updated dataGridView1_CellContentClick to this and it gets me very close to the behaviour I am looking for. If I first left click in column 4 then right click I get my CMS. If I left click any other cell in another column then right click the CMS is no longer there. However I want to be able to just right click a cell in column 4 without having to left click first to create the CMS.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 4)
{
//MessageBox.Show("Hello1");
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
//Assign event handlers
MenuOpenPO.MouseUp += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the Datagrid
dataGridView1.ContextMenuStrip = Menu;
}
else
dataGridView1.ContextMenuStrip = null;
}
I did this in VB ..
Private Sub DGV_CellMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles DGV.CellMouseClick
If e.Button = Windows.Forms.MouseButtons.Right Then
DGV.CurrentCell = DGV.Rows(e.RowIndex).Cells(e.ColumnIndex)
CMS.Show(DGV, DGV.PointToClient(Windows.Forms.Cursor.Position))
End If
End Sub
I figured out my own problem and so will post the solution. Unfortunately I am very new at C# programming and really extremely rusty at it in general. I struggled with the concept of Event handlers (not sure if correct name?), how they were being called and how they all vary depending on if you are using MouseEventArgs, EventArgs, KeyEventArgs, etc. Anyway I digress.
My solution is below. I found the using dataGridView1.MouseUp gave me poor user interaction results requireing 2 of each action for a state change. IE create my ContextMenuStrip when the correct coloumn is right Clicked. Or make it disappear if any other column is right clicked. So use dataGridView1.MouseDown for better results.
dataGridView1.MouseDown += new MouseEventHandler(this.dataGridView_MouseDown);
private void dataGridView_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var ht = dataGridView1.HitTest(e.X, e.Y);
if (ht.ColumnIndex == 4)
{
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
MenuOpenPO.MouseDown += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the DataGridView
dataGridView1.ContextMenuStrip = Menu;
}
else
dataGridView1.ContextMenuStrip = null;
}
}
If you want to create a new form or do something else I used the following code when clicking your ContextMenuStrip I used the following after creating this event handler? MenuOpenPO.MouseDown += new MouseEventHandler(MenuOpenPO_Click);
void MenuOpenPO_Click(object sender, MouseEventArgs e)
{
PO_Form POWindow = new PO_Form();
POWindow.Show();
}

Tool tip problem

I am trying to programmatically set a tooltip for a label (added at runtime) on a UserControl within a form. The button used to trigger the code is on the user control itself. The problem is that when I click the button the tooltip is not assigned. However if I use basically the same code on the parent form and put it behind a button on the parent form, I can assign a tooltip to a label on the parent form. Also if I add the label to user control prior to running it works as well.
The following code is from a button on the user control that sits on the main form.
private void button1_Click(object sender, EventArgs e)
{
Label lblTest = new Label();
lblTest.Text = "Test";
ToolTip tt = new ToolTip();
tt.SetToolTip(lblTest, "ToolTipTest");
this.Controls.Add(lblTest);
lblTest.Location = new Point(10, 10);
}
Any help will be much appreciated.
You can try showing the tooltip manually. Use this code like this:
ToolTip tt = null;
private void button1_Click(object sender, EventArgs e)
{
Label lblTest = new Label();
lblTest.Text = "Test";
tt = new ToolTip();
this.Controls.Add(lblTest);
lblTest.MouseHover += new EventHandler(label_Hover);
lblTest.Location = new Point(10, 10);
}
private void label_Hover(object sender, EventArgs e)
{
tt.Show((Label)sender, "Tooltip");
}
The code in bold are my additions and/or modifications.
Most likely, the ToolTip object goes out of scope after the Click Event. Can you try declaring it outside your Click Event:
ToolTip tt = new ToolTip();
private void button1_Click(object sender, EventArgs e)
{
// and so on...

Categories