NullReferenceException was unhandled - what is null? - c#

I'm trying to access an event in my main form by clicking a button (btnsearch_Click) and everytime I clicked it, it says 'object reference not set to an instance of an object'.
Here is my code:
USER CONTROL
namespace Purchase_Order
{
public partial class Search : UserControl
{
public event EventHandler btnSearchClicked;
public Search()
{
InitializeComponent();
}
private void btnsearch_Click(object sender, EventArgs e)
{
btnSearchClicked(sender, e);
}
}
}
MAIN FORM
namespace Purchase_Order
{
public partial class formMain : Form
{
public formMain()
{
InitializeComponent();
}
private void formMain_Load(object sender, EventArgs e)
{
Search searchbox = new Search();
searchbox.btnSearchClicked += new EventHandler(SearchClicked);
}
void SearchClicked(object sender, EventArgs e)
{
MySqlConnection con = new MySqlConnection(serverstring);
try
{
string query = "SELECT * FROM tblclassification WHERE INSTR(class_name, #search)";
MySqlCommand cmd = new MySqlCommand(query, con);
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
Search content = new Search();
cmd.Parameters.AddWithValue("#search", content.btnsearch.Text);
DataTable dt = new DataTable();
da.Fill(dt);
classification control = new classification();
control.dataGridView1.DataSource = dt;
control.dataGridView1.DataMember = dt.TableName;
panelMain.Controls.Clear();
panelMain.Controls.Add(control);
MessageBox.Show("OK");
}
catch (Exception)
{
throw;
}
finally
{
if (con.State == ConnectionState.Open)
{
con.Close();
}
}
}

You are creating a new instance of your user control in SearchClicked method and you are not registering the event against it.
Search content = new Search();
Also its better if you check whether any control has register your event before raising it like:
private void btnsearch_Click(object sender, EventArgs e)
{
if(btnSearchClicked != null)
btnSearchClicked(sender, e);
}

That means that you haven't got an instance of the type you want to use.
public event EventHandler btnSearchClicked; is just a reference for the "real" object you want to use.
It's like you trying to open a door of a house you only have a blueprint. This isn't really possible (at least not in our universe). You will first need to build the house and then try to enter it. Something like this is the case with your problem.
You will have to read a few tutorials about C#
Edit:
The thing about null is that there is nothing the reference you have is pointing too. If you haven't created anything then there isn't anything to reference...
Because you are trying to use something that doesn't exists (is null) you are getting an exception.

To try and expand a little on Habib's answer (I was going to post this as a comment but it's a little lengthy), you are first creating an instance of Search and registering the event in formMain_Load here:
private void formMain_Load(object sender, EventArgs e)
{
Search searchbox = new Search();
searchbox.btnSearchClicked += new EventHandler(SearchClicked);
}
This is all fine and dandy. However, in SearchClicked, you create a new instance of Search like so:
Search content = new Search();
This is a separate object to the one you created in formMain_Load and you never register the event to this object. It looks like what you want to do is share the Search instance from formMain_Load with the SearchClicked method. To do this, create a property in your codebehind:
public partial class formMain : Form
{
private Search _searchbox;
...
}
Then, in formMain_Load:
private void formMain_Load(object sender, EventArgs e)
{
_searchbox = new Search();
_searchbox.btnSearchClicked += new EventHandler(SearchClicked);
}
Now, you can reuse this object with the event registered in SearchClicked by changing this:
Search content = new Search();
To this:
Search content = _searchbox;
You should find that the exception goes away. Hopefully, this will have provided a little more insight and will help you to understand the cause of the error and how to circumvent it.

Share the Search instance from formMain_Load with the SearchClicked method
MAIN FORM
public partial class formMain : Form
{
private Search _searchbox;
...
private void formMain_Load(object sender, EventArgs e)
{
_searchbox = new Search();
_searchbox.btnSearchClicked += new EventHandler(SearchClicked);
}
void SearchClicked(object sender, EventArgs e)
{
Search content = _searchbox;
MySqlConnection con = new MySqlConnection(serverstring);
try
{
string query = "SELECT * FROM tblclassification WHERE INSTR(class_name, #search)";
MySqlCommand cmd = new MySqlCommand(query, con);
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
cmd.Parameters.AddWithValue("#search", content.btnsearch.Text);
DataTable dt = new DataTable();
da.Fill(dt);
classification control = new classification();
control.dataGridView1.DataSource = dt;
control.dataGridView1.DataMember = dt.TableName;
panelMain.Controls.Clear();
panelMain.Controls.Add(control);
MessageBox.Show("OK");
}
catch (Exception)
{
throw;
}
finally
{
if (con.State == ConnectionState.Open)
{
con.Close();
}
}
}
}
USER CONTROL
public partial class Search : UserControl
{
public event EventHandler btnSearchClicked;
public Search()
{
InitializeComponent();
}
private void btnsearch_Click(object sender, EventArgs e)
{
btnSearchClicked(sender, e);
}
}

Related

How do I pass an instance of a class back through an Event Handler in C#

So I am just getting my head around how Event Handlers work.
I have a WinForms app which has a panel control on the main form. I fill the panel with a login page. The login page will search the DB for the user and check the password is correct when the user clicks the login button. Providing the login details are correct, it then triggers the event handler to tell the main form the login was successful. It should then store the Users details in the main form ready for when the panel is filled with the next control. When the user logs in, it creates an instance of a class called User. How can I pass that User back to the main form so it knows which user is currently logged in?
I hope this makes sense.
This is in the ctrl:
public partial class ctrlLoginPage : UserControl
{
public event EventHandler RegisterButtonClicked;
public event EventHandler LoginButtonClicked;
public User activeUser = new User();
public ctrlLoginPage()
{
InitializeComponent();
}
private void btnRegister_Click(object sender, EventArgs e)
{
RegisterButtonClicked(this, e);
}
public void btnLogon_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection();
con.ConnectionString = "server=DESKTOP-05NVBR2\\SQLEXPRESS;database=GymAppPrototype;UID=sa;password=Mabg123";
con.Open();
SqlDataReader dataReader = null;
SqlCommand myCommand = new SqlCommand("select * from Customer where Email ='" + txtEmail.Text + "';", con);
dataReader = myCommand.ExecuteReader();
while (dataReader.Read())
{
activeUser.forename = dataReader["Forename"].ToString();
activeUser.surname = dataReader["Surname"].ToString();
activeUser.email = dataReader["Email"].ToString();
activeUser.password = dataReader["Password"].ToString();
activeUser.mobile = dataReader["MobileNumber"].ToString();
}
con.Close();
if (txtPassword.Text == activeUser.password)
{
MessageBox.Show("Login Successful");
LoginButtonClicked(this, e);
}
else
{
MessageBox.Show("Login Failed");
}
}
}
It all depends on the way you design your classes and their integration but given the example above you can use a custom EventArgs or better yet a custom EventHandler.
Don't pass 'e' to the 'LoginButtonClicked' event. Instead use custom event args class containing logged in user data. For that you need to define your event handler a tab bit different though:
event EventHandler<MyEventArgs> LoginButtonClicked;
Then define your event args class:
public class MyEventArgs : EventArgs{
public MyEventArgs(User activeUser) {
ActiveUser = activeUser;
}
public User ActiveUser { get; }
}
Now you can pass a new instance of the new class instead of 'e':
LoginButtonClicked(this, new MyEventArgs(activeUser));
The subscriber of the event can then use e.ActiveUser to access the logged in user info, for example:
myLoginPanel.LoginButtonClicked += (s, e) => { /* do something with e.ActiveUser */ };
While it's entirely possible to use the standard event-based messaging in your application, I would question whether it's even necessary here. Depending on your workflow, it might be simplest to show a different form and block until the user logs in/registers, or cancels it:
//MainForm
public void GetUser()
{
using var userWindow = new UserForm();
if (userWindow.ShowDialog(this) == DialogResult.OK)
{
return userWindow.SelectedUser;
}
return null;
}
//UserForm
public class UserForm : Form
{
public User SelectedUser { get; private set; }
private void btnRegister_Click(object sender, EventArgs e)
{
SelectedUser = // registration logic
DialogResult = DialogResult.OK;
Close();
}
private void btnLogon_Click(object sender, EventArgs e)
{
SelectedUser = // logon logic
DialogResult = DialogResult.OK;
Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
}

Form Using User Control Won't Return Value

I am writing a user control. I want it to return a customer number when the user double clicks on the customer. I can't seem to get it to work. The user control is displayed and, on the double click, the data shows in the messagebox but I can't seem to get it to update the value on the main form.
Anytime I try to add a return value into FindCustomerControl_ItemHasBeenSelected, I get an error. It's like it hangs out in the user control and I can't leave it with a return value. So far, this is what I have:
In my main window:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// one close button on the form
Close();
}
private void ShowSelectFromListWidget()
{
// show the user control
var uc = new FindCustomerControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender,
FindCustomerControl.SelectedItemEventArgs e)
{
// once it has been selected, change a label on the screen to show the customer number
var value = e.SelectedChoice;
lblCustomer.Text = value;
}
}
In my user control:
public partial class FindCustomerControl : UserControl
{
public class SelectedItemEventArgs : EventArgs
{ public string SelectedChoice { get; set; } }
public event EventHandler<SelectedItemEventArgs> ItemHasBeenSelected;
public FindCustomerControl()
{ InitializeComponent();}
DataTable dt = new DataTable();
public void btnFind_Click(object sender, EventArgs e)
{ var dt = GetData();
dataGridView1.DataSource = dt; }
//Query database
public DataTable GetData()
{
UtilitiesClass ru = new UtilitiesClass();
string connectionString = ru.getConnectionString();
DataTable dt = new DataTable();
SqlConnection myConnection = new SqlConnection(connectionString);
myConnection.Open();
SqlCommand cmd = new SqlCommand("FindCustomer", myConnection);
cmd.Parameters.AddWithValue("#customer", txtCustomer.Text.Trim());
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter ta = new SqlDataAdapter(cmd);
ta.Fill(dt);
myConnection.Close();
return (dt);
}
private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
var handler = ItemHasBeenSelected;
string choice = dataGridView1[0, e.RowIndex].Value.ToString();
// this shows it
MessageBox.Show("Chosen: " + e.SelectedChoice);
if (handler != null) handler(this, new SelectedItemEventArgs { SelectedChoice = choice });
// I WOULD LIKE TO RETURN A VALUE HERE
}
}
It seems as though this should be common enough but, in spite of my hours of research and debugging, I have been unable to come up with a solution. I do know that uc.ItemHasBeenSelected += uc_ItemHasBeenSelected; in TestForm doesn't seem to ever get executed because I put a breakpoint there.
As I understood I guess you could use application current resources, where you can save any value you wish - in UWP it is something like this:
Application.Current.Resources["Name"] = customerNumber
Then you can cast this value to desired type:
(int)Application.Current.Resources["Name"]
Now you can use this value wherever you want.
Hope than helped in any way.
There is nothing wrong with the user class. It works fine. The problem is that the TestForm needs to start without the FindCustomerControl on it and instantiate the control within the program. It returns the value into the label or wherever else it needs to. Thanks very much to Brad Rem and this post: Return value from usercontrol after user action
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
ShowSelectFromListWidget();
}
private void button1_Click(object sender, EventArgs e)
{
Close();
}
private void ShowSelectFromListWidget()
{
var uc = new FindCustomerControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
this.MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender, FindCustomerControl.SelectedItemEventArgs e)
{
var value = e.SelectedChoice;
lblCustomer.Text = value;
lblMerchant.Focus();
//ClosePrimaryUserControl();
}
private void MakeUserControlPrimaryWindow(UserControl uc)
{
// my example just puts in in a panel, but for you
// put your special code here to handle your user control management
panel1.Controls.Add(uc);
}
private void ClosePrimaryUserControl()
{
// put your special code here to handle your user control management
panel1.Controls.Clear();
}
}

Catch Textbox event from another Form

I have an event in my Form:
public void filterByType_TextChanged(object sender, EventArgs e)
{
dSearch = new D_Search(this);
dSearch.filterD(); }
which calls a function in another class. What I want to do is that I want to notice in my class which Textbox was altered and do something. So there are multiple functions like the one above and they all call the "filterD()" function in my DSearch class. I tried
if (sender == form1.filterByType_TextChanged)
{ sqlCmd = new SqlCommand("SELECT * FROM" } //SQL Statement
}
datTable = new DataTable();
sqlDatAdapter = new SqlDataAdapter(sqlCmd.CommandText,
connection);
sqlDatAdapter.Fill(datTable);
form1.setDataGrid = datTable;
but he can't find "sender" I also tried to create a new Button within the function in my Form and pass it but it doesn't seem to work.
try this -
In form 1
private void textBox1_TextChanged(object sender, EventArgs e)
{
var dSearch = new D_Search(this);
MessageBox.Show(dSearch.filterD(sender));
}
D_Search class
public class D_Search
{
Form1 frm = null;
public D_Search(Form1 frm1)
{
frm = frm1;
}
public string filterD(object sender)
{
string val = String.Empty;
if (sender == frm.textBox1)
{
val = (sender as TextBox).Text;
}
return val;
}
}
also if you want to access filterByType_TextChanged textbox in other class then change its modifier property to Internal

Connecting C# to MySQL and opening connection

This is my simple code just to read something from MySQL. But want I want is to create connection and command when Form is opened and just to open connection when button is clicked and do the rest. But It says:
"The name 'konekcija' does not exist in the current context"
Could someone explain me please.
namespace mysql_windows_console
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
/*========MYSQL KONEKCIJA===========*/
string baza = "server=localhost;database=test;user=root;password=;";
MySqlConnection konekcija = new MySqlConnection(baza);
MySqlCommand comm = konekcija.CreateCommand();
/*========MYSQL KONEKCIJA===========*/
}
private void button1_Click(object sender, EventArgs e)
{
konekcija.Open();
string sql = "SELECT IME,PREZIME FROM tabela";
MySqlDataAdapter adapter = new MySqlDataAdapter(sql,konekcija);
DataTable tab = new DataTable();
adapter.Fill(tab);
dataGridView1.DataSource = tab;
konekcija.Close();
}
}
}
You should declare MySqlConnection outside of the Form_Load EventHandler so you can access it from other methods. Also I would suggest to initialize it in a Form constructor. Since you are using DataAdapter you don't need to use MySqlCommand. Here is the revised code;
namespace mysql_windows_console
{
public partial class Form1 : Form
{
MySqlConnection konekcija;
string baza = "server=localhost;database=test;user=root;password=;"; //so you can access it again if you need it b any chance
public Form1()
{
InitializeComponent();
konekcija = new MySqlConnection(baza);
}
private void button1_Click(object sender, EventArgs e)
{
konekcija.Open();
string sql = "SELECT IME,PREZIME FROM tabela";
MySqlDataAdapter adapter = new MySqlDataAdapter(sql,konekcija);
DataTable tab = new DataTable();
adapter.Fill(tab);
dataGridView1.DataSource = tab;
konekcija.Close();
}
}
}
You're storing konekcija as a local variable. Make it a property like:
private MySqlConnection konekcija { get; set; }
public void Form1_Load(object sender, EventArgs e)
{
//...
this.konekcija = new MySqlConnection(baza);
}
private void button1_click(object sender, EventArgs e)
{
this.konekcija.Open();
//...
}
it simply means that konekcija is not found on the scope of button1_Click.
Minimize the scope of the variable as much as possible. Why not connect only when needed? Example,
const string baza = "server=localhost;database=test;user=root;password=;";
private void button1_Click(object sender, EventArgs e)
{
using (MySqlConnection _conn = new MySqlConnection(baza))
{
using (MySqlCommand _comm = new MySqlCommand())
{
_comm.Connection = _conn;
_comm.CommandText = "SELECT IME,PREZIME FROM tabela";
_comm.CommandType = CommandType.Text;
using (MySqlDataAdapter _adapter = new MySqlDataAdapter(_comm))
{
DataTable _table = new DataTable;
try
{
_adapter.Fill(_table);
dataGridView1.DataSource = _table;
}
catch (MySqlException e)
{
MessageBox.Show(e.Message.ToString());
}
}
}
}
}

Refresh a listbox's datasource

I have a form for my update method, the form is in detail view. Next to the textboxes I have a listbox which shows the names of all the names in the database table. Under the listbox I also have an extra textbox to quick search the name in case user would like to type it in.
When I go to update one of the names, such as changing John to Jonathan, the database updates with the new name as I have checked on sql server, but the name in the listbox does not change! There's a dirty way to fix this by moving the position the listbox is selected on currently to movefirst(). However, under the listbox I have the textbox which is a quick search as I've mentioned, so I go to type Jonathan in the search text box, but nothing appears. However, if I type the former name John, then I get the details of this row in the table.
Is there a way I can fix this?
UPDATE 1:
Ive tried making the listbox datasource null then reassigning it again but it doesen't work. Ive put my code for my update form below.
namespace WindowsFormsApplication1
{
public partial class updateContact : Form
{
public updateContact()
{
InitializeComponent();
}
private void updateContact_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'tblcontactsupdate.tblContacts' table. You can move, or remove it, as needed.
this.tblContactsTableAdapter.Fill(this.tblcontactsupdate.tblContacts);
}
private void btnUpdateContact_Click(object sender, EventArgs e)
{
int x;
Program.da.UpdateCommand = new SqlCommand("Update tblContacts SET FIRSTNAME = #FIRSTNAME, LASTNME = #LASTNME WHERE ID = #ID", Program.cs);
Program.da.UpdateCommand.Parameters.Add("#FIRSTNAME", SqlDbType.VarChar).Value = fIRSTNAMETextBox.Text;
Program.da.UpdateCommand.Parameters.Add("#LASTNME", SqlDbType.VarChar).Value = lASTNMETextBox.Text;
Program.da.UpdateCommand.Parameters.Add("#ID", SqlDbType.VarChar).Value = iDTextBox.Text;
Program.cs.Open();
x = Program.da.UpdateCommand.ExecuteNonQuery();
Program.cs.Close();
if (x >= 1)
{
MessageBox.Show("Record(s) has been updated");
Program.ds.Clear();
Program.da.Fill(Program.ds);
txtfindUpdatecontact.Text = "";
//lbupdateContact.DataSource = null;
//lbupdateContact.DataSource = this.tblcontactsupdate.tblContacts;
}
}
private void txtfindUpdatecontact_TextChanged(object sender, EventArgs e)
{
if (!txtfindUpdatecontact.Text.Equals(""))
{
this.tblContactsBindingSource.Filter = "FIRSTNAME = '" + txtfindUpdatecontact.Text + "'";
}
else
{
this.tblContactsBindingSource.RemoveFilter();
}
}
private void lbupdateContact_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void iDTextBox_TextChanged(object sender, EventArgs e)
{
}
private void fIRSTNAMETextBox_TextChanged(object sender, EventArgs e)
{
}
private void lASTNMETextBox_TextChanged(object sender, EventArgs e)
{
}
}
}
You will have to set the DataSource of your listbox one more time after updating the source.
Something like below: It's my data:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
public class MyDataSource
{
public static List<Person> Persons = new List<Person>
{
new Person{Age=30,Name="Ram"},
new Person{Age=33,Name="Rahim"},
};
}
then in the form's constructor you can do:
listBox1.DataSource = MyDataSource.Persons;
listBox1.DisplayMember = "Age";
then for updation, something like below:
private void button1_Click(object sender, EventArgs e)
{
MyDataSource.Persons[0].Age = 45;
listBox1.DataSource = null;
listBox1.DataSource = MyDataSource.Persons;
listBox1.DisplayMember = "Age";
}
This is just an example change code according to your need.
If your DataSource is a DataTable, all you have to do is calling AcceptChanges(), like this:
listBox.DataSource = null;
((DataTable)listBox.DataSource).AcceptChanges();

Categories

Resources