I am new to C# and I am working on a project for my studies and I have multiple logins an Administrator and Teacher. This project has multiple winforms the 2 I am needing help with is my Login and the Main form after the user logs in.
I have already created the logins and they work but I need to disable a button called btnMarks int he Main form the Administrator cannot have access to this button.
I have tried if statements but I can't seem to make it work. I am using radio buttons for logins as well as the Administrator and Teachers logins have their own tables in the database. I can only use Entities not SQLconnections it is part of the project for my studies.
Please help
Below is my user login form code.
private void btnLogin_Click(object sender, EventArgs e)
{
//A check to make sure both fields have an entry
if(txtUsername.Text == "" || txtPassword.Text == "")
{
MessageBox.Show("Please provide a Username and Password!");
return;
}
//This is to call the boolean radiobuttons are checked
radioButtons();
//Teachers login
if (rbTeachers.Checked)
{
int Username = Convert.ToInt32(txtUsername.Text);
SchoolDBEntities db = new SchoolDBEntities();
var tid = from t in db.Teachers
where t.TID == Username
&& t.Password == txtPassword.Text
select t;
if (tid.Any())
{
MessageBox.Show("You are now logged in as a Teacher!");
this.Hide();
Main tss = new Main();
tss.Show();
}
else
{
MessageBox.Show("Incorrect Username or Password!");
}
}
//Administrator login
if (rbAdmin.Checked)
{
int Username = Convert.ToInt32(txtUsername.Text);
SchoolDBEntities db = new SchoolDBEntities();
var aid = from a in db.Administrators
where a.AID == Username
&& a.Password == txtPassword.Text
select a;
if (aid.Any())
{
MessageBox.Show("You are now logged in as Administrator");
this.Hide();
Main tss = new Main();
tss.Show();
}
else
{
MessageBox.Show("Incorrect Username or Password");
}
}
}
Below is my Main form, I need the btnMarks button disabled for Administrators.
I am unsure where to put the code to disable this button as well. If I could be able to call the radio button from the login form please show me how.
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private void Main_Load(object sender, EventArgs e)
{
}
private void btnMarks_Click(object sender, EventArgs e)
{
frmStudentMarks marks = new frmStudentMarks();
marks.ShowDialog();
}
Thanks
I would recommend you making something like SessionManagement object to manage current session ( logged user, user rights etc. ).
public static class SessionManagement
{
static UserEntity sessionUser = null;
public static void LoggedAs(UserEntity user)
{
sessionUser = user;
}
// other methods/fields to manage session
}
After doing this you can just set session for currently logged user :
var tid = from t in db.Teachers
where t.TID == Username
&& t.Password == txtPassword.Text
select t;
SessionManagement.LoggedAs((UserEntity)tid); // make some explicit operators or something.
Now you have full control over who is logged in and you can check it's rights so all you have to do is to check it after InitializeComponent() method call :
public Main()
{
InitializeComponent();
btnMarks.Enabled = !SessionManagement.CurrentUser.IsAdministrator;
}
EDIT:
You've asked if there's something else you should do for this code to make it works. Answer is yes. This answer is basically a scheme for you to work something out. But since it's not an easy thing to do I'll explain it in somewhat more details.
Firstly, you have 2 types of Entity: Teacher and Administrator and you need to make one "unified" entity ( I named it UserEntity ). This unified entity should be convertible from both Teacher and Administrator entity.
My recommendation in code :
public class UserEntity
{
string _username;
public string Username
{
get { return _username; }
}
bool _isAdministrator;
public bool IsAdministrator
{
get { return _isAdministrator; }
}
public UserEntity(Administrator entity)
{
_isAdministrator = true;
_username = entity.AID;
}
public UserEntity(Teacher entity)
{
_isAdministrator = false;
_username = entity.TID;
}
public static explicit operator UserEntity(Administrator entity)
{
return new UserEntity(entity);
}
public static explicit operator UserEntity(Teacher entity)
{
return new UserEntity(entity);
}
}
Now you can do somehting like UserEntity userEntity = (UserEntity)teacher;
Next thing to do is to update SessionManagement by adding new method into it :
public static void LoggedAs(UserEntity entity)
{
if(sessionUser != null)
throw new InvalidOperationException("Cannot be logged 2 times with the same session");
sessionUser = entity;
}
And a property :
public static UserEntity CurrentUser
{
get { return sessionUser; }
}
Now all you have to do is to combine all of these into one huge chunk of code :
private void btnLogin_Click(object sender, EventArgs e)
// parts of your code till this line :
SchoolDBEntities db = new SchoolDBEntities();
var tid = from t in db.Teachers
where t.TID == Username
&& t.Password == txtPassword.Text
select t;
Teacher teacher = tid.FirstOrDefault();
if(teacher != null)
{
SessionManagement.LoggedAs((UserEntity)teacher);
}
// do the same with Administrator
Now since SessionManagement is static object you can use it everywhere inside your application and it will persist with all stored data meaning you can use :
public Main()
{
InitializeComponent();
btnMarks.Enabled = !SessionManagement.CurrentUser.IsAdministrator;
}
You need to maintain a static class for that in which you can add that the current user's type.
Kindly go through the following url
https://stackoverflow.com/a/14599474/1526972
The command button, by default, has a public access level so you can access and disable it from login form before to call the Show() method in this way:
tss.yourButtonName.Enabled = false;
Hope this help.
Christian
Related
I am trying to create a function GetUserID() which returns the userID, which I have inserted into a label so I can use it in other forms.
But when I try to convert the label to int32 the label always seems to be empty. I think its because of where the function is placed in my code.
See:
private void Loginbtn_Click(object sender, EventArgs e)
{
var LoginFunction = new LoginFunction();
var DataTable = new DataTable();
DataTable = LoginFunction.Login(Usernametxt.Text.Trim(), Passwordtxt.Text.Trim());
int UserID = Convert.ToInt32(DataTable.Rows[0]["USER_ID"]);
if (DataTable.Rows.Count == 1)
{
CalculatorMain calculatorMain = new CalculatorMain();
MainMenu mainMenu = new MainMenu();
UserIDlbl.Text = Convert.ToString(UserID);
MessageBox.Show("ID = " + UserID);
this.Hide();
mainMenu.Show();
}
else
{
MessageBox.Show("You entered the wrong username or password");
}
}
public int GetUserID()
{
int UserID;
if (Int32.TryParse(UserIDlbl.Text, out UserID))
{
UserID = Convert.ToInt32(UserIDlbl.Text);
}
else
{
MessageBox.Show("Error, Label for UserID could not be parsed");
}
return UserID;
}
I'm not sure where else I can put this function to get it to work.
Here is the code to call the function which is used in a separate form.
private void WorkoutHistoryForm_Load(object sender, EventArgs e)
{
Login login = new Login();
int UserId = login.GetUserID();
this.sETSTableAdapter.Fill(this.gymDataSet.SETS, UserId);
}
I keep thinking there must be a better way to do this instead of storing the UserID in a label but I'm not sure how.
I would create a public class with a public field to store the UserID.
For example. let's say you have the UserID in an int variable as you have described. Now let's say you have created a public static class called Common defined with a public static field of type int called ID.
You can now store the UserID in the static field of the class:
Common.ID = UserID
Later, when you want to access the UserID from some other form, just do this:
string UserID = Common.ID
Easy peasey.
Of course, you don't need to do this in a separate class... your form itself is a class, and you can create your public field there, and call it like
Form1.UserID
Or whatever the name of your original form is where you captured the UserID...
Ok, so my background is in VBA-Msaccess (SQL Server Backend). Have been developing for around 6 years. Our company has recently been hit massively by the limitations of MsAccess so have decided to start developing in C# and integrating into our existing back end.
I can quite easily solve this issue but I just want to make sure I am solving it in the most ideal way.
So I have a login form (Win forms) that is passing a username and password into a login class. The login class then compares the information against data in SQL server and returns a true or false value based on whether there is a matching record found. Within the class, I have then setup properties to contain information about the user ie their permission levels.
The issue I am having is, while this works fine because I am instantiating an object from login.cs these permission levels are then only contained within the created object and are not accessible after the login page. Presumably, I need to be storing this permission information in static or global variable but I just wish to know the most optimized and well-formed way of doing this!
C# Login Form
public partial class frmLogin : Form
{
public frmLogin()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
string userName = txtUsername.Text;
string passWord = txtPassword.Text;
Login login = new Login("","");
string user = txtUsername.Text;
string pass = txtPassword.Text;
if (login.IsLoggedIn(user, pass))
{
MessageBox.Show(login.adminUser.ToString());
frmMainMenu form = new frmMainMenu();
this.Hide();
form.ShowDialog();
}
else
{
MessageBox.Show("Log in failed please try again!");
}
}
}
Login.cs
public class Login
{
public string UserName { get; set; }
public string PassWord { get; set; }
private bool aUser;
private bool tLeader;
public bool adminUser
{
get
{
return aUser;
}
set
{
aUser = value;
}
}
public bool teamLeader
{
get
{
return tLeader;
}
set
{
tLeader = value;
}
}
public Login(string user, string pass)
{
this.UserName = user;
this.PassWord = pass;
}
private void ClearText(string user, string pass)
{
user = string.Empty;
pass = string.Empty;
}
public bool IsLoggedIn(string user, string pass)
{
if (string.IsNullOrEmpty(user))
{
MessageBox.Show("Enter the user name!");
return false;
}
else
{
ConnectionStrings connstring = new ConnectionStrings();
SqlConnection sqlConn = new SqlConnection(connstring.connNameUser);
sqlConn.Open();
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.Connection = sqlConn;
sqlCmd.CommandType = System.Data.CommandType.Text;
sqlCmd.CommandText = "SELECT username, password, door_order_admin, super_user from dbo.[user] WHERE username = #user and password = #pass";
sqlCmd.Parameters.AddWithValue("#user", user);
sqlCmd.Parameters.AddWithValue("#pass", pass);
SqlDataReader reader = sqlCmd.ExecuteReader();
if (reader.Read())
{
if (Convert.ToInt16(reader["door_order_admin"]) == 1)
{
aUser = true;
}
else
{
aUser = false;
}
if (Convert.ToInt16(reader["super_user"]) == 1)
{
tLeader = true;
}
else
{
tLeader = false;
}
return true;
}
else
{
ClearText(user, pass);
return false;
}
}
}
}
Any help in pointing me in the right direction would be massively appreciated. Like I said, I want to make sure I am starting out on the right foot as opposed to writing a bunch of code only to realize further down the line that I could have done it much better.
Thanks in advance!
I created a class User, which contain simple variables as shown below:
public class User
{
public string username; //Unique usernames
public string password;
}
I then instantiate a list of an object in another class:
List<User> user = new List<User>();
user.Add(new User {username = "admin", password = "123"});
How is it possible for me to retrieve the password's value by searching for the username using a foreach loop? I am probably just confused but this is what I came up with:
foreach(var item in user)
{
if(item.Equals(username_input))
{
//I try to store the password into a string pass_check
pass_check = item.password;
}
}
if (user_input.Equals(pass_check))
{
Console.WriteLine("Login successful");
}
Sorry if this seems like a dense question to anyone out there, still a beginner trying to learn!
You're pretty close..
if(item.username.Equals(username_input))
You need to check the property of the item in this case which is username.
You could even shorten it to:
foreach(var item in user)
{
if(item.username.Equals(username_input)
&& user_input.Equals(item.password))
{
Console.WriteLine("Login successful");
break; // no need to check more.. or is there?
}
}
You can get really fancy using Linq:
if (user.Any(i => i.username.Equals(username_input)
&& user_input.Equals(i.password))
{
Console.WriteLine("Login successful");
}
As juharr noted in the commend, best practices for exposing values from class/objects is to use Properties not Fields..
public class User
{
// not best practices
public string username;
// best practices
public string password { get; set; }
}
Even fancier:
using System.Linq;
public static class Extensions
{
// Extension method that works on List<User> to validate user && PW
// - returns true if user exists and pw is ok, else false
public static bool CheckUserPassword(this List<User> users, string user_name, string pw)
{
// add null checks for users, user_name and pw if you are paranoid of know your customers ;o)
return users.Any(u => u.username == user_name && u.password == pw);
}
}
public class User
{
public string password;
public string username; //Unique usernames
}
internal class Program
{
private static List<User> users = new List<User> { new User { username = "admin", password = "123" } };
static void Main(string[] args)
{
// use extension method like this:
var isValid = users.CheckUserPassword("Hello","Jeha");
Console.WriteLine($"user 'admin' with pw '8888' => {users.CheckUserPassword("admin", "8888")}");
Console.WriteLine($"user 'admin' with pw '123' => {users.CheckUserPassword("admin", "123")}");
Console.ReadLine();
}
}
Extension Methods can be executed on the this-part - in this case only on List<User>s. The Extensionmethod uses Linq to find if Any (at least 1) user of this name and pw exists.
when i try to set a specific Item in dataGrid , it changes all other item's values to that same value. I'm not sure if it's a bug or i done something wrong. Here is my code:
(Datagrid is in another window (Main window), so i called a function in that window to edit the value)
private void AAbutton1_Click(object sender, RoutedEventArgs e)
{
Account selected = new Account();
if (textBox2.Text != null)
selected.username = textBox2.Text;
if (textBox12.Text != null)
selected.password = textBox12.Text;
if (locationTxtBox2.Text != null)
selected.location = locationTxtBox2.Text;
MainWindow.Instance.editAccount(selected);
MainWindow.Instance.updateData();
MainWindow.Instance.needsSave = true;
}
And here is the function in the main window:
public void editAccount(Account acc)
{
Account acc2;
Account selected = (Account)dataGrid.SelectedItem;
acc2 = Manager.accounts.ElementAt(Manager.accounts.FindIndex(a=> a == selected));
acc2.username = acc.username;
acc2.password = acc.password;
acc2.location = acc.location;
}
I really couldn't find a solution for this problem.
And here is the Account class in case you need it:
public class Account
{
public String username { get; set; }
public String password { get; set; }
public String location { get; set; }
public Account(String username,String password, String location)
{
this.username = username;
this.password = password;
this.location = location;
}
public Account()
{
}
}
Just to mention , i use Mahapps.metro controls.
I was right! I read your mind.
This isn't a WPF question, a binding question, or a DataGrid question. It's a "how do references work in C#?" question. It's a good question.
On file load, you start with a list of encrypted Accounts, but in decryption, you copy all the decrypted properties of each one of the accounts into the same instance of Account, and add that one instance multiple times to the list. The decrypted ones are all the same instance. You start off OK, but then you go off the rails in DecryptAccounts().
Here's the bug:
public static void DecryptAccounts()
{
// Hmmm. What's he planning to do with this?
Account holder = new Account(null, null, null);
accounts.Clear();
foreach (Account acc in Encryptedaccounts)
{
// HERE IT IS. This is the same instance of holder on every
// iteration. After file load, every Account in accounts is the
// same object as every other.
// You need to create a new Account object for each account.
holder.username = Decrypt(acc.username, user.Decryptedpassword);
holder.password = Decrypt(acc.password, user.Decryptedpassword);
holder.location = Decrypt(acc.location, user.Decryptedpassword);
accounts.Add(holder);
}
}
public static void LoadFromFile()
{
if (File.Exists(Path.Combine(appdata, folder, file)))
{
Encryptedaccounts = JsonConvert.DeserializeObject<List<Account>>(File.ReadAllText(Path.Combine(appdata, folder, file)));
}
DecryptAccounts();
}
Here's the fix
Manager.cs
public Account DecryptAccount(Account acc)
{
return new Account {
username = Decrypt(acc.username, user.Decryptedpassword),
password = Decrypt(acc.password, user.Decryptedpassword),
location = Decrypt(acc.location, user.Decryptedpassword)
};
}
public static void DecryptAccounts()
{
accounts.Clear();
foreach (Account acc in Encryptedaccounts)
{
accounts.Add(DecryptAccount(acc));
}
}
// You've got the same issue here
private static void EncryptAccounts()
{
Encryptedaccounts.Clear();
foreach (Account acc in accounts)
{
Encryptedaccounts.Add(EncryptAccount(acc));
}
}
public Account EncryptAccount(Account acc)
{
return new Account {
username = Encrypt(acc.username, user.Decryptedpassword),
password = Encrypt(acc.password, user.Decryptedpassword),
location = Encrypt(acc.location, user.Decryptedpassword)
};
}
Some other issues here. Not bugs, but life will be easier if you do stuff the "proper WPF way":
Manager.accounts should be of type ObservableCollection<Account>. Then it will automatically notify the DataGrid whenever you add or remove items from it and you won't have to do this updateData() thing to manually refresh the grid all the time.
Manager and Account both ought to implement INotifyPropertyChanged and fire notifications on their properties when their values change. In C#6, this is very simple:
using System.Runtime.CompilerServices;
using System.ComponentModel;
// ... snip ...
public event PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
Then your properties look like this:
private String _username = null;
public String username {
get { return _username; }
set {
if (value != _username)
{
_username = value;
OnPropertyChanged();
}
}
}
When they do that, anything you bind them to in the UI will be notified whenever you change the values. You'll be able to set properties on the selected grid item and the UI will update without any grid refresh or anything -- it'll just know. Very convenient.
I would like to compare UserName and Pass entered by an user with me Local Database.
When I Tried to request on my database, I saw I had to put simple quotes into my request, such as
SELECT * FROM Administrator
WHERE UserName = 'FSelva';
The problem is, when I check the database, I do it like that:
if((username == admin.UserName) && (password == admin.Pass))
{
this.Close();
MainPage retourpageprincipale = new MainPage();
retourpageprincipale.Show();
}
else
{
Admin.**** is the database ones and username & password are variables which catch what the user entered.
When I'm doing step by step, admin.UserName is Null, such as admin.Pass.
I think adding simple quotes like:
if((username == "'" && admin.UserName && "'") && (password == "'" && admin.Pass && "'"))
could fix my problem. But I can't do like this.
Is anybody knows what's the syntax please?
edit:
This is the entire code:
namespace WpfApplication3
{
public partial class Window1 : Window
{
private Database1Entities1 conn = new Database1Entities1();
public Window1()
{
InitializeComponent();
}
private void ConnectionClick(object sender, RoutedEventArgs e)
{
var username = UserNameBox.Text;
var password = PasswordBox.Text;
con.Database.Connection.Open();
Administrator admin = new Administrator();
if((username == admin.UserName) && (password == admin.Pass))
{
this.Close();
MainPage retourpageprincipale = new MainPage();
retourpageprincipale.Show();
}
else
{
MessageBox.Show("Combo Admin/Password non valide!
}
Where Administrator is my admin table:
public partial class Administrator
{
public int Id { get; set; }
public string UserName { get; set; }
public string Pass { get; set; }
public bool IsSuperAdmin { get; set; }
}
When I Step by Step this code, at line:
if((username == admin.UserName) && (password == admin.Pass))
username value is RSans (what I wrote into the IHM)
password valus is 1234abcd (What I wrote into the IHM)
admin.UserName valus is Null
admin.Pass is Null.
Moreover,
admin value is WpfApplication3.Administrator
conn value is WpfApplication3.Database1Entities1
If you need some others informations, i'll edit my post again.
Edit2:
This is my Database1Entities1 full code:
namespace WpfApplication3
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class Database1Entities1 : DbContext
{
public Database1Entities1()
: base("name=Database1Entities1")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DBSet<Administrator> Administrator { get; set; }
}
The problem in your code lies by reading the Information from the database. If both admin.* properties are null, it means that they are either null in the database or you don't set them properly.
Furtermore, you do not need simple quotes. They are used in SQL to show that the cell is of type string. Once you read the information to an object in C#, you will see that the quotes are gone. It is easiest to debug your code and step from line to line to see what is wrong.
Update
Since you are using Entity Framework, here is how you can retrieve information from the Database:
Your Database1Entities1 is the so called DbContext. You can use it to query the database. It should have a property Administrators. If it doesn't, you can add the following part to the Database1Entities1 class:
public DbSet<Administrator> Administrators { get; set; }
You can then use the your conn object to query the database, e.g.
var admin = conn.Administrators.SingleOrDefault(a => a.UserName == "FSelva");
Since you are already querying the database, you could add the whole check (including the password) in one step:
var admin = conn.Administrators.SingleOrDefault(a => a.UserName == "FSelva" && a.Password == "...");
// if there is no match, the admin object will be NULL here
Update 2
You do not need to open the connection manually. Entity Framework will do that for you. So you can delete the line:
con.Database.Connection.Open();