I am trying to repeat an exercise from the book ("C# 7.0: All-in-One-for-Dummies") which shows how authentification and authorization works in WinForms. And everything compiles and there are no issues during runtime but it doesn't works as intended.
Let me explain in details:
I have three buttons (Sales, Accountancy and HR) and I have the code which allows only the member of the appropriate group to be able to see this button (by the way, buttons' .Visible = false). I made an additional User (with the user rights and not administrator) and Groups for experimentation and added this User to each Group consequently. What I mean. I added the user to the group and then check the result. After this I deleted user from the group and add him to the next one. And so on.
The result is that sometimes I can see all buttons and some times I can't see only one button (but not the intended one) and it seems like my code doesn't affect a thing or affect it in a weird manner. I have no idea what is going on. My Windows is 7 Home Advanced and I am working with groups and users via CMD with net localgroup and net user. I read Microsoft description for IsInRole method and it seems like I did everything correct because if you indicate string with the name of the group it means the exact group in your Windows. Here is the code itself:
using System;
using System.Windows.Forms;
using System.Security.Principal;
namespace AuthentificationAndAuthorization
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
WindowsIdentity userIdentity = WindowsIdentity.GetCurrent();
WindowsPrincipal userRights = new WindowsPrincipal(userIdentity);
if (userRights.IsInRole("Accountancy")) AccountingButton.Visible = true;
else if (userRights.IsInRole("Sales")) SalesButton.Visible = true;
else if (userRights.IsInRole("HR")) ManagerButton.Visible = true;
else if (userRights.IsInRole(WindowsBuiltInRole.Administrator)) AccountingButton.Visible = true; SalesButton.Visible = true; ManagerButton.Visible = true;
}
}
}
Thanks in advance for any suggestions!
Here's your problem: don't use if without braces. It's what caused Apple's 2014 iOS security nightmare.
Change this:
if (userRights.IsInRole("Accountancy")) AccountingButton.Visible = true;
else if (userRights.IsInRole("Sales")) SalesButton.Visible = true;
else if (userRights.IsInRole("HR")) ManagerButton.Visible = true;
else if (userRights.IsInRole(WindowsBuiltInRole.Administrator)) AccountingButton.Visible = true; SalesButton.Visible = true; ManagerButton.Visible = true;
to this:
if (userRights.IsInRole("Accountancy"))
{
this.AccountingButton.Visible = true;
}
else if (userRights.IsInRole("Sales"))
{
this.SalesButton.Visible = true;
}
else if (userRights.IsInRole("HR"))
{
this.ManagerButton.Visible = true;
}
else if (userRights.IsInRole(WindowsBuiltInRole.Administrator))
{
this.AccountingButton.Visible = true;
this.SalesButton.Visible = true;
this.ManagerButton.Visible = true;
}
....or better yet, do this:
Boolean isAdmin = userRights.IsInRole(WindowsBuiltInRole.Administrator);
this.AccountingButton.Visible = userRights.IsInRole("Accountancy") || isAdmin;
this.SalesButton .Visible = userRights.IsInRole("Sales") || isAdmin;
this.ManagerButton .Visible = userRights.IsInRole("HR") || isAdmin;
Also, avoid using magic strings: if you move those role-names to be const String values in a static class then your code will be easier to maintain in future.
After I tried some debugging and did nothing else it started to work as I expect all of a sudden. This is cryptic...Also I found that builin Administrator can see all buttons no matter what you code tells. Ok. Nevermind.
Related
I am trying to remake my C#/WPF take on the Simon game by making it more clean and use classes now that I finally understand classes. Before I ask my question - here is some sample code from my old version as a reference.
public MainWindow()
{
InitializeComponent();
RedButton.IsEnabled = false;
BlueButton.IsEnabled = false;
GreenButton.IsEnabled = false;
YellowButton.IsEnabled = false;
}
//Use TextBlock clearing to say "Watch the pattern", "Your Turn" and then an empty box between games
private void StartButton_Click(object sender, RoutedEventArgs e)
{
_IsError = false;
RedButton.IsEnabled = true;
BlueButton.IsEnabled = true;
GreenButton.IsEnabled = true;
YellowButton.IsEnabled = true;
StartButton.IsEnabled = false;
if (randomPattern.Count == 0)
{
randomPattern.Add(random.Next(0, 4));
randomPattern.Add(random.Next(0, 4));
}
StatusBox.Text = "Watch the Pattern.";
ShowPattern();
}
So, my question is: Would it be good practice for me to use IsElement as a property/attribute of class "Buttons" instead of writing all of their statements out individually? And if I should add them to the class, I think my next question comes down to - how would I implement it into the class? I know simply typing only "IsEnabled = <T/F>" won't do anything.
EDIT:
To show you what I mean:
Should I have IsEnabled as a class property like such:
class Button {
IsEnabled = false;
}
var greenButton = new Button()
//Some code that lets the player start the game by clicking a button
greenButton.IsEnabled = true;
Or should I keep my IsEnabled statements outside of a class like in my old version of the code? Note - my old version of the code, there are no classes that I made - only functions.
IsEnabled should be placed into Button class as it is property or feature of button.
public class Button {
public IsEnabled { get; set; }
}
The above class has high cohesion as it is focused on what it should be doing. It has only methods/properties, fields relating to the intention of the class.
I am trying to disable some menu strip items while a textbox is displaying "Calculating...". Once that value goes away, I wish to re-enable the menu items. Its purpose is not to interrupt MD5/CRC32 calculations. So far, I've tried various method of code, and have had no luck so far. What's listed below should work, but for some reason it does not. Any help would be appreciated.
// THIS PART WORKS
if (boxMD5.Text.Contains("Calculating") == true)
{
openROMToolStripMenuItem.Enabled = false;
saveROMDataToolStripMenuItem.Enabled = false;
asTXTToolStripMenuItem.Enabled = false;
asHTMLToolStripMenuItem.Enabled = false;
}
// THIS PART DOES NOT WORK
else if (boxMD5.Text.Contains("Calculating") == false)
{
openROMToolStripMenuItem.Enabled = true;
saveROMDataToolStripMenuItem.Enabled = true;
asTXTToolStripMenuItem.Enabled = true;
asHTMLToolStripMenuItem.Enabled = true;
}
I can't quite tell you why the code isn't doing what you expect, but I can make a suggestion that will change your approach and may help achieve your goal at the same time. What you are trying to do shouldn't be to disable the menu when the textbox contains "Calculating" but instead you should disable the menu while the calculations are being performed. From a user/UI perspective, these are the same thing, but the inner-workings of your program know better.
Based on you PasteBin code, try this:
private void openROMToolStripMenuItem_Click(object sender, EventArgs e)
{
//Other code omitted for brevity
if (File.Exists(OpenFileDialog1.FileName))
{
UpdateUI("Calculating...");
backgroundWorker1.RunWorkerAsync(OpenFileDialog1.FileName);
}
//Other code omitted for brevity
}
and
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateUI(e.Result.ToString());
}
where the new method UpdateUI() looks like this:
void UpdateUI(string hash)
{
var calculating = hash == "Calculating...";
if (!calculating)
{
progressBar1.Value = 0;
}
openROMToolStripMenuItem.Enabled = !calculating;
saveROMDataToolStripMenuItem.Enabled = !calculating;
asTXTToolStripMenuItem.Enabled = !calculating;
asHTMLToolStripMenuItem.Enabled = !calculating;
boxMD5.Text = hash;
}
Also, notice how you are able to just put !calculating in the if statement rather than calculating == false. This is because the value is already true or false so you don't have to compare it to anything to figure that out. The same thing applies to your original code but you don't need it anymore with this approach.
After hunting the internet for two days and not finding a solution that I can understand properly I have to ask on here for an answer.
I have a windows forms application that was written in vb.net and works fine. I have decided to rewrite this in c# which I thought wouldn't be too much of a problem but ...
I have two classes in the project :
FormJobs & AppJobs
FormJobs contains methods and functions that modify the forms in some way.
AppJobs contains methods and functions for everything else (Checks,Scanning and so forth).
On my main form (FrmStart) the On_load event uses a function from AppJobs to check that the network is up (public bool CheckNetConnection) and then Checks to make sure that the root save folder exists (public void CheckRoot).
If CheckNetConnection is false or CheckRoot does not exist then a method in the FormJobs class sets some buttons to disabled, some labels to display information as to what has gone wrong and also sets the height of the form.
The above works in VB.net but I keep getting a StackOverflowException or NullReferenceException with the C# code.
I know the reason for the Exceptions is because the two classes and the form all keep calling each other so I know that I need to remove this code but I am not sure how to let each class and the form access each other. It is obviously bad design as I`m just starting to learn C# so any help on this would be much appreciated.
But my main questions are:-How do I get a form to access multiple classes?
Allow the classes to access each other?
Let the classes make changes to a form?
FrmStart Code
AppJobs Appjobs = new AppJobs();
private void FrmStart_Load(object sender, EventArgs e)
{
KeyPreview = true;
if (Appjobs.CheckNetConnection(this) == true)
{
Appjobs.CheckRoot(this);
}
AppJobs Code
public class AppJobs
{
FormJobs Formjobs = new FormJobs();
public string AppRoot = Properties.Settings.Default.DefaultFolder;
public string DefaultDevice = Properties.Settings.Default.DefaultScanner;
public bool NoDirectory = false;
DialogResult MsgBoxQuestion;
public bool CheckNetConnection(Form StartForm)
{
IPHostEntry ServerIP = new IPHostEntry();
bool ConnectedToServer = false;
string CurrentRoot = "MyServer";
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
try
{
IPHostEntry DNSTest = Dns.GetHostEntry(CurrentRoot);
if (DNSTest.AddressList.Length > 0)
{
ConnectedToServer = true;
}
else
{
ConnectedToServer = false;
}
}
catch (System.Net.Sockets.SocketException ex)
{
ConnectedToServer = false;
}
}
return ConnectedToServer;
}
public void CheckRoot(Form StartForm)
{
if (string.IsNullOrEmpty(AppRoot))
{
Formjobs.SetHeight(StartForm);
return;
}else if(AppRoot == "0")
{
Formjobs.SetHeight(StartForm);
return;
}
else
{
if ((!Directory.Exists(AppRoot)))
{
NoDirectory = true;
MsgBoxQuestion = MessageBox.Show(AppRoot + " is set, but the directory does not exist." + Environment.NewLine
+ Environment.NewLine + "Would you like to create the folder now?", "Root folder missing", MessageBoxButtons.YesNo);
if (MsgBoxQuestion == DialogResult.Yes)
{
Directory.CreateDirectory(AppRoot);
NoDirectory = false;
}
else
{
MessageBox.Show("You will not be able to use this program until you create a root folder.", "No root folder selected",MessageBoxButtons.OK);
}
}
}
}
}
FormJobs Code
public class FormJobs
{
AppJobs Appjobs = new AppJobs();
public void SetHeight(Form StartForm)
{
if (Appjobs.AppRoot == null | Appjobs.AppRoot == "0") {
if (Appjobs.DefaultDevice == null | Appjobs.DefaultDevice == "0") {
if (StartForm.Controls["MenuStrip1"].Visible == true) {
StartForm.Height = 167;
StartForm.Controls["LblNoRoot"].Visible = true;
StartForm.Controls["LblNoRoot"].Location = new Point(0, 24);
StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
StartForm.Controls["LblNoDevice"].Visible = true;
StartForm.Controls["LblNoDevice"].Location = new Point(0, 48);
StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
StartForm.Controls["BtnOkTickets"].Enabled = false;
StartForm.Controls["BtnQueryTickets"].Enabled = false;
StartForm.Controls["BtnSearch"].Enabled = false;
}else
{
StartForm.Height = 147;
StartForm.Controls["LblNoRoot"].Visible = true;
StartForm.Controls["LblNoRoot"].Location = new Point(0, 9);
StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
StartForm.Controls["LblNoDevice"].Visible = true;
StartForm.Controls["LblNoDevice"].Location = new Point(0, 33);
StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
StartForm.Controls["BtnOkTickets"].Enabled = false;
StartForm.Controls["BtnQueryTickets"].Enabled = false;
StartForm.Controls["BtnSearch"].Enabled = false;
}
}
One of the causes of your problems is that everyone is changing your StartForm. Apart from that this spaghetti makes it difficult to understand, it certainly doesn't help to make your classes reusable and maintainable if your Startform changes.
It seems to me, that AppJobs is meant to decide what the form should look like (for instance it decides that the StartForm should change height), while FormJobs performs the calculations needed to change this height. StartForm apparently is just allowing to let everyone make changes to him.
A better design would be that StartForm would not ask AppJobs to change its size, and to ask the operator whether a folder should be generated. Instead if ought to ask appJobs for advise: "Which height do you think I should have". after that it could ask FormJobs: "Please adjust my height according to this specification"
FormJobs should trust StartForm that it has gathered the correct information about how a StartForm ought to look like. FormJobs should not ask AppJobs for any information: "Hey AppJobs, StartForm asked me to change its appearance to certain specifications, but I'm not certain whether StartForm has done its job correctly. Please tell me if these specifications are correct, and give me some missing information")
The correct division into tasks would be:
AppJobs specifies the format of any StartForm according to its internal state (a.o. AppRoot, and existence of certain folders)
StartForm is the one who displays all items. He decides who to ask for specifications, and what to do with the returned specifications. He is also the only one who communicates with operators
FormJobs is a class that apparently knows all elements from a StartForm. If you will only have one type of StartForm, then Appjobs should be part of the Startform class. If you think there might be several different Startform classes, all with the same elements that ought to be manipulated similarly, shouldn't all these StartForm classes be derived from a FormJobs class?
Anyway, redesign without everyone causing to manipulate StartForm
Apparently there are a limited number of StartForm layouts depending on AppRoot, defaultDevice etc. You seem to be missing some "else" after your if, so this list might not be accurate. Still you will get the idea:
enum StartFormLayouts
{
DefaultDevice0,
AppRoot0,
Other,
}
// class that specifies the layout of any startform
class AppJobs
{
private bool IsAppRoot0
{
get{return Appjobs.AppRoot == null || Appjobs.AppRoot == "0";}
}
private bool IsDefaultDevice0
{
get{return Appjobs.DefaultDevice == null || Appjobs.DefaultDevice == "0";}
}
public StartFormLayoug GetPreferredLayout()
{
if (this.IsAppRoot0)
{
if (this.IsDefaultDevice)
{
return StartFormLayout.DefaultDevice0;
}
else
return StartFormLayout.AppRoot0;
}
else
{
return StartFormLayout.Other;
}
}
public bool ShouldAskDirectoryCreation()
{
return (!this.IsAppRoot0 && !Directory.Exists(AppRoot));
}
}
Note that this class doesn't need StartForm, nor AppJobs. It could work with any class that wants to know if it should ask for DirectoryCreation. Since it also does not speak any language, even a Chinese StartForm could use it. After all, the StartForm is the only one who knows what language it speaks and what to do if a certain layout is requested.
Furthermore, did you notice that I used a double || to use a Boolean OR instead of a bitwise or?
And I use statements like if (a) instead of if(a=true) a C# Boolean is a real Boolean, in contradiction to Booleans in C and C++.
The class of all kinds of forms that should be able to be layout according to the requested layout contains the functions similar to your
It depends a bit of whether you decide to let it be a base class of StartForm or StartForm itself. If you want it to handle every form class that has the required controls, consider of using an interface:
public Interface IStartForm
{
public int Height {get; set;}
public Label LabelNoRoot {get;}
public Label LabelNoDevice {get; }
public Button BtnTickets {get;}
...
This way you can set the size of any form that has these labels and buttons, even if they have different names than those strings you use.
But again: if you ever only want to size StartForm, then this should be a function in StartForm.
public SetHeight(StartFormLayout layout, IStartForm startForm)
{
switch (layout)
{
case StartFormLayout.DefaultDevice0:
if (startForm.MenuStrip.Visible)
{
startForm.Height = ...;
startForm.LabelNoRoot.Location = ...
// etc
}
else
{
...
Noticed that because of this separation of concerns the AppJobs and FormJobs don't have to know each other. AppJobs and FormJobs also don't really have to know what 'StartForm` is, only that it has the labels and buttons etc that it needs to change.
class StartForm : Form, IStartForm
{
public Label LabelNoRoot {get{return this.label1; } }
...
private void FrmStart_Load(object sender, EventArgs e)
{
AppJobs layoutdesigner = new AppJobs(...);
StartFormLayout layoutdesigner = layouter.GetPreferredLayout();
FormJobs layouter = new FormJobjs();
layouter.SetHeight(this)
}
Noticed that my form didn't have a label named "LabelNoRoot", but a Label1 that should function as a LabelNoRoot. Also: because I used types instead of string, You can be certain that I can't handle a label as if it was a button. I can't by accident try to press the label. Something that could easily been done when you were using strings to identify the items you want to layout.
Extending the comments: you just remove the new part in your FormJobs and AppJobs classes.
leaving the code in i.e. in the FormJobs class like : AppJobs appObj;
Then in your main form create at some point a FormJobs obj and an AppJobs obj and set its property.
I.e. in main Form:
AppJobs appObj = new AppJobs();
FormJobs formObj = new FormJobs();
formObj.appObj = appObj;
Tho I must say i dont like that approach you are taking with this...
You should think of another way or at least refactor your code that FormJobs does not need AppJobs methods and vice versa in a way that all calls to FormJobs and AppJobs come from your main form.
I have an applications that install two other application which has a "Help" option. Each of these application has a common help file but the contents should be displayed based on the index selected for the application in the "Table of Contents". If i open one application, the help of that particular application should be displayed.
My code looks like this for Appl1.
private void Help_Click(Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
if (System.IO.File.Exists(new PlugInConstants().HELP_FILE_Path))
{
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
new PlugInConstants().HELP_FILE_Path,
System.Windows.Forms.HelpNavigator.TableOfContents, "Appl1");
}
else
{
System.Windows.Forms.MessageBox.Show(m_objLanguage.ERR_HELP_NOT_FOUND.Replace
("%1", m_objGlobalConfig.HelpFilename));
}
CancelDefault = false;
}
and looks like this for Appl2
private void HelpToolStripMenuItem_Click(object sender, EventArgs e)
{
helpToolStripMenuItem.Enabled = false;
string helpFilePath;
helpFilePath = new TrayConstants().HELP_FILE_Path;
if (System.IO.File.Exists(helpFilePath))
{
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
helpFilePath, System.Windows.Forms.HelpNavigator.TableOfContents, "Appl2") ;
}
else
{
if (m_helpPage == null)
m_helpPage = new HelpPage();
m_helpPage.ShowDialog();
}
helpToolStripMenuItem.Enabled = true;
}
from this i can only see the Content page of common helpfile, but not the particular application help that is selected.
Now i did run Appl1 but still i can see the main MyAppbut not Appl1that is automatically selected and the contents that is displayed to right.
I am using VS 2010,C#, win forms
thanks in advance
I believe that your issue is that you are accessing the wrong value in the HelpNavigator enum. Looks like it should be Topic, not TableOfContents.
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
helpFilePath, System.Windows.Forms.HelpNavigator.Topic, "Appl2") ;
http://msdn.microsoft.com/en-us/library/system.windows.forms.helpnavigator.aspx
Introduction of problem:
I have two forms Home.cs and Login.cs. I have ToolStripMenuItems in Home.cs, Admin will log-in from the Login.cs form. On form_load (Home.cs) event I had made two menu items disabled.
addToolStripMenuItem.Enabled = false;
editToolStripMenuItem.Enabled = false;
After successful login I want to enable those menu items in Home.cs. But is not able to figure out how to do that in C#. I thought I can do something like this:
private Home hm = null;
and then in authentication event I can do....
hm.addToolStripMenuItem.Enabled = true;
Problem:
But this is not working, and this is not the right way to handle this situation. Plz help......
You could simply call the Login.cs from the Load event of the Home.cs form/class , just like that:
public void Home_Load(...params...)
{
Login log = new Login();
if(log.ShowDialog() == DialogResult.Ok)
{
// enable the menu here
}
else
{
// let the menu disabled or exit the application here
}
}