Access TextBox from Static method in C#? - c#

I have 2 classes: MyForm and Database
In MyForm I have a method to change a label text to show error:
public void printError(string text){
label1.Text = text;
}
My Database class needs to access that method too, so I make it static:
public static void printError(MyForm form, string text){
form.label1.Text = text;
}
Now the problem is, how do I call that method from Database class?
This question I found said that I need to pass MyForm into Database's contructor like this:
class MyForm : Form{
Database db;
public Form(){
db = new Database(this);
}
}
class Database{
MyForm form;
public Database(MyForm f){
form = f;
}
...
//then I can access the printError like this
MyForm.printError(form, "You got error");
}
I tried that and it freezes the form. Any other solution?
Thanks

Here is a very simple example of how you can achieve this without your data layer knowing about your UI:
class MyForm : Form
{
Database db;
public Form()
{
db = new Database(this);
}
public void DoSomething()
{
var errors = db.Login("", "");
if (errors.Any())
label1.Text = errors.First(); // Or you can display all all of them
}
}
class Database
{
public List<string> Login(string username, string password)
{
var errors = new List<string>();
if (string.IsNullOrEmpty(username))
errors.Add("Username is required");
if (string.IsNullOrEmpty(password))
errors.Add("Password is required");
[...]
return errors;
}
}

Like #Matthew Ferreira and others have stated the design is not idea, but here's something to get you started.
class MyForm : Form
{
public void SomeMethod()
{
var dataAccess = new Repository();
dataAccess.ExecuteQuery();
if (dataAccess.Exceptions.Any())
{
// display your error messages
form.label1.Text = dataAccess.Exceptions.Select(x => x.ToString());
}
}
}
class Repository
{
private readonly HashSet<Exception> _exceptions = new HashSet<Exception>();
public IEnumerable<Exception> Exceptions
{
get { return _exceptions; }
}
public int ExecuteQuery()
{
var numberOfRecordsAffected = 0;
try
{
// do something
}
catch (Exception ex)
{
// normall catching exceptions is a bad idea
// and you should really catch the exception at the
// layer best equiped to deal with it
_exceptions.Add(ex);
}
// but, for the purpose of this example we might want to add some logic to try the query on another database ????
try
{
// do something
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
return numberOfRecordsAffected;
}
}

You need to look up "seperation of concerns". Its really bad to mix your UI code with you Database Access Layer (DAL). Better to bind the UI to business objects that are populated via a DAL.
To let the UI know about an error you could simply use a delegate.
namespace OperationErrorDelegate
{
public delegate void OperationErrorHandler(Exception ex);
public class DAL
{
public event OperationErrorHandler ReportError;
public void DoDALOperationThatCausesError()
{
try
{
int i = 1;
int j = 0;
int k = i/j;
}
catch (Exception ex)
{
ReportError(ex);
}
}
}
}
Add this code to the form:
using System ;
using System.Windows.Forms;
namespace OperationErrorDelegate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DAL DAL = new DAL();
DAL.ReportError += new OperationErrorHandler(DAL_OperationErrorProgress);
DAL.DoDALOperationThatCausesError();
}
private void DAL_OperationErrorProgress(Exception ex)
{
label1.Text = ex.Message;
}
}
}

Assuming the OP's requirement is to display an error message in a label, when the credentials are wrong:
private void btn_login_Click(object sender, EventArgs e)
{
MySqlConnection con = new MySqlConnection("server=localhost;uid=root;password=abc;database=mydb");
MySqlCommand cmd = new MySqlCommand("select * from emp where name='" + textBox1.Text + "'and pwd='" + textBox2.Text + "'",con);
con.Open();
MySqlDataReader dr = cmd.ExecuteReader();
if (dr.Read())
{ //successful
//navigate to next page or whatever you want
}
else
Label1.Text("Invalid userid or password");
con.Close();
}
And if you need error message for wrong data type (the user input string but the database column is Integer), then use validations at client side. You dont need to do it at backend, since that will be a burden.
You can use regular expressions for that in the button_click itself.

Related

Passing Object from Class to Windows Form

I've got four main files in play here: Form 1, which is called frmLogin, Form2, which is called frmUserHome, a class called DbConnector.cs and a class called BankAccount.cs
The program works as follows: Form1 calls method from DBConnector -> DB Connector method fills an object and it's variables which is instantiated in DBConnector. This object is of class BankAccount.cs
Then, after the method finishes and if successful, Form1 instantiates Form2 and it opens. In form2 is where I want to access the object that was created in the DBconnector class. How do I do this? I've tried instantiating new objects of BankAccount.cs in there, I've tried all these different constructor stuff I've seen on here. Nothing seems to be working.
For reference: the object is populated when it runs in Dbconnector. However, when I try to receive data when I use it in Form2, all of the fields are NULL.
I've made countless adjustments from reading different posts on here so the code is rightfully a mess now and probably extremely unorganised.
Here's the main portions of my code below:
Form1
namespace BankingSystem
{
public partial class frmLogIn : Form
{
public BankAccount myBankAccount = new BankAccount();
dbConnector newConnector;
public frmLogIn()
{
InitializeComponent();
timerMain.Enabled = true;
timerMain.Start();
}
private void btnLogIn_Click(object sender, EventArgs e)
{
try
{
newConnector.CheckDetailsLogin(accountNumTextBox.Text, pinNumTextBox.Text);
frmUserHome UserHome = new frmUserHome();
MessageBox.Show("Success! Happy Banking!");
UserHome.ShowDialog();
}
catch
{
MessageBox.Show("Failed - incorrect login details.");
}
}
}
}
DBConnector Class:
namespace BankingSystem
{
public class dbConnector
{
Boolean isCorrect = false;
private static SQLiteConnection sqlconnConnection;
public BankAccount myBankAccount = new BankAccount();
public DataSet myAppDataSet = new DataSet(); // created for you to use and push data into
public dbConnector(string strFilePath)
{
try
{
sqlconnConnection = new SQLiteConnection("Data Source=" + strFilePath);
}
catch (Exception ex)
{
throw new Exception("DbConnector initialisation unsuccessful:\n" + ex.Message);
}
}
public void CheckDetailsLogin(string strAccno, string strPin)
{
// this is where check ou the boiler plate code and adjst to my APP.
try
{
DataTable dtUser = new DataTable();
sqlconnConnection.Open();
string strQuery2 = #"SELECT * FROM Accounts WHERE Account_Number='"+ strAccno +"' AND PIN='"+ strPin +"';"; // example of a parametrised SQL statement.
SQLiteCommand sqlcomCommand2 = new SQLiteCommand(strQuery2, sqlconnConnection);
SQLiteDataAdapter sqldatadptAdapter = new SQLiteDataAdapter(sqlcomCommand2); // local SQL data Adaptor
try
{
sqldatadptAdapter.Fill(dtUser);
}
catch (Exception ex)
{
// Exception will the "thrown"/Raised when there was a problem
throw new Exception($"SELECT unsuccessful:\n{ex.Message}");
}
finally
{
sqlconnConnection.Close();
}
if (dtUser.Rows.Count == 0)
{
// the record set comes back with no records found, an empty Datatable with no rows
// means there was no data matching your query
throw new Exception("No Such Bank user found");
}
else
{
// change to your applications needs
// Rows[0] - we are expecting at least 1 row, and its basically an array so we address
// the first element with 0
// Rows[0]["fieldnamefromDB"] <- referencing the column in the DB
//this.strID = strUserID;
myBankAccount.AccountNumber = dtUser.Rows[0]["Account_Number"].ToString();
myBankAccount.AccountPin = dtUser.Rows[0]["PIN"].ToString();
myBankAccount.AccountBalance = Convert.ToDecimal(dtUser.Rows[0]["Balance"]);
myBankAccount.AccountHolder = dtUser.Rows[0]["First_Name"].ToString();
myBankAccount.AccountAddress = dtUser.Rows[0]["Home_Address"].ToString();
myBankAccount.MyAccountGUID = dtUser.Rows[0]["GUID"].ToString();
if (myBankAccount.AccountNumber == strAccno && myBankAccount.AccountPin == strPin)
{
isCorrect = true;
}
else
{
isCorrect = false;
}
//myLocalBankAccUsr
}
}
catch (Exception ex)
{
// exception thrown for the whole method or function
throw new Exception($"User(string):\n{ex.Message}");
}
}
BankAccount.cs class
namespace BankingSystem
{
public class BankAccount
{
private string accountNumber;
private decimal accountBalance;
private string accountHolder;
private string accountPhoneNumber;
private string accountAddress;
private string accountPin;
private string myAccountGUID;
// private string AccountHolderGUID;
// private string AccountTypeGUID;
public string AccountNumber
{
get { return accountNumber; }
set { accountNumber = value; }
}
public decimal AccountBalance
{
get { return accountBalance; }
set { accountBalance = value; }
}
public string AccountHolder
{
get { return accountHolder; }
set { accountHolder = value; }
}
public string AccountPhoneNumber
{
get { return accountPhoneNumber; }
set { accountPhoneNumber = value; }
}
public string AccountAddress
{
get { return accountAddress; }
set { accountAddress = value; }
}
public string AccountPin
{
get { return accountPin; }
set { accountPin = value; }
}
public string MyAccountGUID
{
get { return myAccountGUID; }
set { myAccountGUID = value; }
}
public Boolean CanWithDrawAmount(decimal AmountToTransfer)
{
if (AmountToTransfer > this.AccountBalance){
return false;
}else
{
return true;
}
}
public void UpdatePIN()
{
// connect to bank DB connector
// send it the new pin
// SQL update command
}
}
}
Here is Form2:
namespace BankingSystem
{
public partial class frmUserHome : Form
{
public frmUserHome()
{
InitializeComponent();
tabMainForm.Appearance = TabAppearance.FlatButtons;
tabMainForm.ItemSize = new Size(0, 1);
tabMainForm.SizeMode = TabSizeMode.Fixed;
timerMain.Enabled = true;
timerMain.Start();
}
private void frmUserHome_Load(object sender, EventArgs e)
{
labelWelcome.Text = "Welcome "; //newMainBank.AccountHolder;
}
The 'labelWelcome.Text = "Welcome" is where I want the name stored inside the object to be used. So it should ideally access the BankAccount class, access the AccountHolder field and use that field to concat onto the end of the 'Welcome' text. However, it just shows 'Welcome' and no name on the end, when I run the program (because all values get reset to null in form2.. for some reason)
Below I have updated the code for Form1 and Form2.
Form 1
namespace BankingSystem
{
public partial class frmLogIn : Form
{
dbConnector newConnector;
public frmLogIn()
{
InitializeComponent();
newConnector = new dbConnector(**pass str file path**);
timerMain.Enabled = true;
timerMain.Start();
}
private void btnLogIn_Click(object sender, EventArgs e)
{
try
{
newConnector.CheckDetailsLogin(accountNumTextBox.Text, pinNumTextBox.Text);
frmUserHome UserHome = new frmUserHome(newConnector.myBankAccount);
MessageBox.Show("Success! Happy Banking!");
UserHome.ShowDialog();
}
catch
{
MessageBox.Show("Failed - incorrect login details.");
}
}
}
}
Form 2
namespace BankingSystem
{
public partial class frmUserHome : Form
{ public BankAccount _bankAccount;
public frmUserHome(BankAccount bankAccount)
{
InitializeComponent(); _bankAccount = bankAccount;
tabMainForm.Appearance = TabAppearance.FlatButtons;
tabMainForm.ItemSize = new Size(0, 1);
tabMainForm.SizeMode = TabSizeMode.Fixed;
timerMain.Enabled = true;
timerMain.Start();
}
private void frmUserHome_Load(object sender, EventArgs e)
{
labelWelcome.Text = "Welcome "+ bankAccount.AccountHolder;
}

Implementing async/task/await in a multi-tier windows form application

I would appreciate some help with async/Task implementation in my Windows Form application.
In this app, I retrieve data from the datacenter SQl server database using WCF services, so part of this code runs on the client and some on the datacenter server where it retrieves the data and returns it. I’d like to optimize the code using async/Task on the clien or server or preferably both. The example code starts with a Windows Form with a button, when the button is clicked it gets a value from the database, displays it and updates a local variable.
I’m not clear if I can simply implement async/Task and Task.Run in the first button click event or whether the code should be cascaded through all methods, or something in between. I'm also not clear on how to handle the wcf service.
I’ve created a simplified example of the code, pretty close to sequentially.
In this code, the return value updates the windows form. I’d like to see how this code is optimized using async/Task await for this purpose and what would be different if the code did not return a value.
public partial class Form1 : Form
{
int returnvalue = 0;
public Form1()
{
InitializeComponent();
}
private void btnGetResult_Click(object sender, EventArgs e)
{
int rowcount = ChangeProductPrice(.05m);
txtResult.Text = rowcount.ToString();
}
private int ChangeProductPrice(decimal priceincrease)
{
int rv = MyData.WebServiceObject.ChangePrice(priceincrease);
UpdateLocalVariables(rv);
return rv;
}
private void UpdateLocalVariables(int rv)
{
returnvalue = rv;
}
}
public static class MyData
{
private static IMyDataWCFService _webserviceobject = null;
public static IMyDataWCFService WebServiceObject
{
get
{
if (_webserviceobject is null)
{
//...code to initialize it
}
return _webserviceobject;
}
}
}
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMyDataWCFService
{
[OperationContract]
int ChangePrice(decimal priceincrease);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyDataWCFService : IMyDataWCFService
{
private MyDataService _serviceObject = null;
private MyDataService ServiceObject
{
get
{
if (_serviceObject == null)
{
_serviceObject = new MyDataService();
}
return _serviceObject;
}
}
public int ChangePrice(decimal priceincrease)
{
return ServiceObject.ChangePrice(priceincrease);
}
}
public class MyDataService //running on server
{
public int ChangePrice(decimal priceincrease)
{
int rows = 0;
SqlConnection conn = null;
try
{
conn = this.GetSqlConnection();
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = conn;
cmd.CommandText = "mysp_UpdatePrice";
cmd.Parameters.Add(new SqlParameter("#Rate", priceincrease));
conn.Open();
rows = cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
ProcessError(ex);
}
finally
{
if (conn != null)
ReturnSqlConnection(conn);
}
return rows;
}
private SqlConnection GetSqlConnection()
{
//dostuff;
return new SqlConnection();
}
private void ProcessError(Exception ex)
{
//dostuff;
}
private void ReturnSqlConnection(SqlConnection conn)
{
//dostuff;
}
}

Calling a class's method from Windows form application

I am trying to call a method with the same name of its class (AHPModel) but accepts an integer argument, from a Windows form by creating an instance of the class, but it gives me an error of "Object reference not set to an instance of an object", please could you help!
Thank you.
In the Form:
private void button6_Click(object sender, EventArgs e)
{
try
{
AHPModel model = new AHPModel(3, 3);
model.AddCriteria(criteria);
model.AddCriterionRatedChoices(0, night);
model.AddCriterionRatedChoices(1, act);
model.AddCriterionRatedChoices(2, cost);
model.CalculateModel();
GeneralMatrix calcCriteria = model.CalculatedCriteria;
GeneralMatrix results = model.ModelResult;
GeneralMatrix choices = model.CalculatedChoices;
}
catch (System.Exception excep)
{
MessageBox.Show(excep.Message);
}
}
My Class
public class AHPModel
{
public AHPModel(int n)
{
}
public void CalculateModel()
{
CalculatePriorities();
CalculateChoices();
CalculateFinalResult();
}
}
That is happening because you have not created the parmeterized constructor for
AHPModel model = new AHPModel(3, 3);
You can do the AHPModel model = new AHPModel(3, 3); when you have class with constructor like below
public class AHPModel
{
public AHPModel(int n)
{
}
public AHPModel(int n,int n)
{
}
public void CalculateModel()
{
CalculatePriorities();
CalculateChoices();
CalculateFinalResult();
}
}
I just realised that I was passing null values of arrays that are locally declared to the method. But now I am able to access the targeted method from my windows form.

Pushing new Collection item to a GridView in a different class

I have a LogOperations class, which is a singleton, and it contains a collection of my Log object and a few methods for adding new Logs to the collection. Log only has 3 properties; Message, Exception, and Time.
On the only form in my application, I get a reference to LogOperations. Then I use its Collection<Log> logs property to build a data table out of each entry and throw that into a GridControl (gdcErrorLogs).
gdcErrorLog.DataSource = logOps.GetLogsAsDataTable(); //Popualtes dt with each Log in logs
I have a bunch of other classes that perform all sorts of functions for the form, and whenever these functions catch an error I write that error to my LogOperations's collection.
How can I make my form's grid automatically update with this new entry each time a Log gets added? I'm thinking of adding a reference to the form in my LogOperations class, and then at the end of my Add functions I'd just re-set the form's grid to be this new DataTable?
public class AddNewLogEventArgs : EventArgs
{
public Log log { get; private set; } // Error on compile see below
public AddNewLogEventArgs(Log newLog) // Error on compile see below
{
log = newLog;
}
}
class LogOps
{
public delegate void AddNewLogHandler(object sender, AddNewLogEventArgs e);
public event AddNewLogHandler OnAddNewLog;
private Collection<Log> _logs;
private static LogOps _singleton;
private LogOps()
{
_logs = new Collection<Log>();
}
public static LogOps Instance
{
get
{
if (_singleton == null)
_singleton = new LogOps();
return _singleton;
}
}
public void AddLog(string text)
{
Log newLog = new Log(text);
_singleton._logs.Add(newLog);
OnAddNewLog(new AddNewLogEventArgs(newLog), null);
}
public void AddLog(Log log)
{
Log newLog = log;
_singleton._logs.Add(newLog);
OnAddNewLog(new AddNewLogEventArgs(newLog), null);
}
public void AddLog(Exception ex)
{
Log newLog = new Log(ex.Message, ex.InnerException);
_singleton._logs.Add(newLog);
OnAddNewLog(new AddNewLogEventArgs(newLog), null);
}
public DataTable GetLogsAsDataTable()
{
DataTable dt = new DataTable();
dt.Columns.Add("Time");
dt.Columns.Add("Error");
dt.Columns.Add("Exception");
int i = 0;
foreach (Log log in _logs)
{
dt.Rows.Add();
dt.Rows[i].SetField("Time", log.Time);
dt.Rows[i].SetField("Error", log.Err);
dt.Rows[i].SetField("Exception", log.Ex.Message);
i++;
}
return dt;
}
}
Edit Code sample was heavily modified since trying out King King's solution
The two flagged lines in the AddNewLogEventArgs give me these two errors:
Error 1 Inconsistent accessibility: property type 'PrepareDeployment.Models.Log' is less accessible than property 'PrepareDeployment.Processes.AddNewLogEventArgs.log'
Error 2 Inconsistent accessibility: parameter type 'PrepareDeployment.Models.Log' is less accessible than method 'PrepareDeployment.Processes.AddNewLogEventArgs.AddNewLogEventArgs(PrepareDeployment.Models.Log)'
You should do something like this:
public delegate void AddNewLogEventHandler(object sender, AddNewLogEventArgs e);
public event AddNewLogEventHandler AddNewLog;
public class AddNewLogEventArgs : EventArgs {
public Log {get; private set;}
public AddNewLogEventArgs(Log log){
Log = log;
}
}
protected virtual void OnAddNewLog(AddNewLogEventArgs e){
AddNewLogEventHandler handler = AddNewLog;
if(handler != null) handler(this, e);
}
public void AddLog(Exception ex) {
Log newLog = new Log(ex.Message, ex.InnerException);
singleton._logs.Add(newLog);
OnAddNewLog(new AddNewLogEventArgs(newLog));
}
public void AddLog(string text) {
Log newLog = new Log(text);
singleton._logs.Add(newLog);
OnAddNewLog(new AddNewLogEventArgs(newLog));
}
In your main form (with your grid), you can do something like this:
LogOperations.Instance.AddNewLog += (s,e) => {
var dt = (DataTable) gdcErrorLog.DataSource;
dt.Rows.Add(e.Log.Time, e.Log.Err, e.Log.Ex.Message);
};
NOTE: You should remove the private frmMain frm; in your old class.

ComboBox isnt displaying information from database

I have to create an automated teller machine app to access a database that contians sample customer records. I am having a problem displaying the account numbers from the accountInformation table (database), in my comboBox. I am pretty sure that I created the database connection correctly, and I thought that the code I have would dispay the numbers in the comboBox, so I'm not sure what the problem is. Is there something in comboBox properties that I need to change?
Here is my code:
using SQLDll;
namespace WindowsFormsApplication14
{
public partial class Form1 : Form
{
private Connection myConnection;
private Statement myStatement;
private ResultSet myResultSet;
String databaseURL = "http://www.boehnecamp.com/phpMyAdmin/razorsql_mysql_bridge.php";
public Form1()
{
InitializeComponent();
try
{
//connect to database
SQL sql = new SQL();
myConnection = sql.getConnection(databaseURL);
//create Statement for executing SQL
myStatement = myConnection.createStatement(databaseURL);
}
catch (Exception)
{
Console.WriteLine("Cannot connect to database server");
}
//close statement and database connection
myStatement.close();
myConnection.close();
}
private void Form1_Load(object sender, EventArgs e)
{
loadAccountNumbers();
}
public void setText(string text)
{
}
//load account numbers to ComboBox
private void loadAccountNumbers()
{
//get all account numbers from database
try
{
myResultSet = myStatement.executeQuery("SELECT accountNumber FROM accountInformation");
// add account numbers to ComboBox
while (myResultSet.next())
{
accountNumberComboBox.Items.Add(myResultSet.getString("accountNumber"));
}
myResultSet.close(); // close myResultSet
}//end try
catch (Exception)
{
Console.WriteLine("Error in loadAccountNumbers");
}
}//end method to loadAccountNumbers
I would recommend putting everything in one method. It is more readable and you can ensure everything gets closed even if an exception occurs.
public partial class Form1 : Form
{
private Connection myConnection;
private Statement myStatement;
private ResultSet myResultSet;
String databaseURL = "http://www.boehnecamp.com/phpMyAdmin/razorsql_mysql_bridge.php";
public Form1()
{
InitializeComponent();
loadAccountNumbers();
}
//load account numbers to ComboBox
private void loadAccountNumbers()
{
SQL sql = new SQL();
using (myConnection = sql.getConnection(databaseURL))
using (myStatement = myConnection.createStatement(databaseURL))
using (myResultSet = myStatement.executeQuery("SELECT accountNumber FROM accountInformation"))
{
// add account numbers to ComboBox
while (myResultSet.next())
accountNumberComboBox.Items.Add(myResultSet.getString("accountNumber"));
}
}
I found out what was wrong. I knew I had to call loadAccountNumbers() somewhere but I wasn't sure where. I placed it in public Form1() and that fixed my problem! Thanks for all the help.

Categories