I'm attempting to read a text file and delete a user entered string. I cannot get it to report a message if the string does not exist.
I cannot explain everything I've tried to this point, it's been many things. I know there is nothing in it's current form that would give me the results I expect, but I've tried many many things and this is currently where it's at. For the code that is there, it's doing everything I'm telling it to do.
if (rButtonDelete.Checked)
{
bool isValid = txtID.Text.Length < 5;
if (txtID.Text == "")
{
lbOne.Items.Add("You must enter a fixture to delete.");
}
else
if(!isValid==false)
{
lbOne.Items.Add("Enter full fixture ID to delete.");
}
else
{
var oldLines = System.IO.File.ReadAllLines(#"F:\09 Quality\CMM Fixtures\fixtures.txt");
var newLines = oldLines.Where(lines => !lines.Contains(txtID.Text));
System.IO.File.WriteAllLines(#"F:\09 Quality\CMM Fixtures\fixtures.txt", newLines);
lbOne.Items.Add(txtID.Text + " was deleted.");
}
}
As stated above, as it exists now, it does everything I am telling it to do. I just need to report that a string being searched for does not exist if in doesn't. No matter what I type into the text box, it tells me it's been deleted, even if it doesn't exist.
How about this:
if (oldLines.Count() == newLines.Count())
{
lbOne.Items.Add(txtID.Text + " does not exist.");
}
else
{
lbOne.Items.Add(txtID.Text + " was deleted.");
}
Related
When the same "ID" is entered in the textbox, another output should be created.
ex.
when I enter 123, 124, 125 (individually), they output " Success Login!", and when I enter 123 or either of the "IDs" that are already entered, it will give me this output "Success Logout!".
I have tried storing it to a variable -- public static String StoreUserID; but I think this is the wrong approach since I will be using textbox.Clear(); so it outputs "user logged out" when I dont enter anything :(
public static String StoreUserID;
///
private void IDTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (IDTextBox.Text != "")
{
FlashMessage.Text = IDTextBox.Text + " Success Login! " + Environment.NewLine + DateTime.Now.ToString();
IDTextBox.Clear();
}
else if (StoreUserID == IDTextBox.Text)
{
FlashMessage.Text = IDTextBox.Text + " Success Logout! " + Environment.NewLine + DateTime.Now.ToString();
}
else
{
FlashMessage.Text = "No ID Entered";
}
}
I expected it to output another message when the same "ID" is entered, but it only outputs "Success Logout!" when there is no text in the textbox.
You're missing one vital thing: you don't remember who is logged in and who is not because you never set your StoreUserID variable. Because your program only seems capable of remembering a single person at a time, which isn't very useful for a DTR program, I've changed it to a Dictionary. Dictionary is like an array but indexed by a string not an integer and it provides us a way to store when they logged in:
private Dictionary<string, DateTime> _loggedInUsers = new Dictionary<string, DateTime>(); //need to add this at top of file: using System.Collections.Generic;
private void IDTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (string.IsNullOrWhiteSpace(IDTextBox.Text)) //usually better than comparing with ""
{
FlashMessage.Text = "No ID Entered";
return;
}
var user = IDTextBox.Text.Trim(); //remove header/trailing spaces
IDTextBox.Clear(); //might as well do this now
if(_loggedInUsers.ContainsKey(user))
{
//how long were they logged in?
TimeSpan ts = (DateTime.Now - _loggedInUsers[user]);
_loggedInUsers.Remove(user); //log them out
FlashMessage.Text = $"{user} successfully logged out at {DateTime.Now}, you were logged in for {ts.TotalHours} hours, which is also {ts.TotalSeconds}. You earned {ts.TotalHours * 200} at your rate of 200$/hr";
}
else
{
_loggedInUsers[user] = DateTime.Now; //log them in at this time
FlashMessage.Text = $"{user} successfully logged in. Go do some work and then log out and I'll tell you how long you were";
}
}
}
I also added some other fluff showing you how to use string interpolation but the most critical part is; we put the user into the dictionary when we log them in and take them out when they log out. The string is entered into the dictionary along with the time of login. The string is later used to look up the time of login and gives us a way to calc the hours worked. Removing the user from the dictionary logs them out; it is the "presence" or "not presence" of a user id in the dictionary that determines if they are logged in (presence) or out (not presence)
I put a Trim() command in there because spaces make different strings. If user types 123 upon login, and 123 on "logout" he will log in again because the spaces make these users different things. Users love to do daft things like this and we must protect against them where possible
I am currently working on an application for the company I work for. Part of this application deals with discrepancy reporting and sending emails out when a new discrepancy number has been created. First, it is important to realize that I am redeveloping this application from VB.NET to C#. In the old application the developer chose to read an XML file for a few email addresses. I've read to use alternate options unless the XML file is full of information. This is not intended to be an opinionated question and sorry if this sounds like one. However, I am looking for the correct way of doing this. Should these email addresses be kept in a database table for easy adding/deleting or is there a more standardized way to do this? Please find the current code below.
public void PrepareEmail(string subject, string message)
{
if (MessageBox.Show(#"Are you sure you want to save and send Discrepancy Report: " + tbxDRNumber.Text + #"?\n Click YES to save\n Click NO to cancel", #"Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
SendEmail(subject, message);
}
}
public Array AddEmail()
{
string[] dRemail = { "", "", "" };
if (File.Exists(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml"))
{
XmlReader emailDocument = new XmlTextReader(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml");
while (emailDocument.Read())
{
var type = emailDocument.NodeType;
switch (type)
{
case XmlNodeType.Element:
if (emailDocument.Name == "DRCreatedAddEmail")
{
dRemail[0] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRActionNeededAddEmail")
{
dRemail[1] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRPendingAddEmail")
{
dRemail[2] = emailDocument.ReadInnerXml();
}
else
{
MessageBox.Show(#"The file: 'DREmailAddresses.xml' was not found at: \\fs01\Applications\EMS-Manager");
}
break;
}
}
}
return dRemail;
}
public void SendEmail(string subjectText, string bodyText)
{
string[] email = (string[])AddEmail();
//object oOutlook = default(Microsoft.Office.Interop.Outlook.Application);
var oMessage = default(MailItem);
Activator.CreateInstance(Type.GetTypeFromProgID("Outlook.Application"));
if (subjectText == "New Discrepancy Created. DR" + tbxDRNumber.Text + " ")
{
oMessage.To = email[0];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + " - Action Needed")
{
oMessage.To = email[1];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + "DR Pending Approval")
{
oMessage.To = email[2];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
}
There isn't necessarily anything wrong with flat file configuration files. It really depends on your use case. How often do the emails in the file change? How many entries are there? Do users of the application edit the list? Are there any complex rules where different addresses will get sent different emails?
If this just a simple mailing list that doesn't change often it's probably fine to use the xml file. You could also just create an email distribution list to send to instead, like ApplicationDiscrepancyReport. That way you can just manage the recipients in active directory. If you stick with the XML email addresses, at the very least I would try to get rid of the hardcoded path to the xml file located on your file share.
If the email addresses change a lot, and are changed by users of the application, I would recommend moving this to a SQL database. If the recipients list is changing frequently you may want to add tracking of when these addresses get edited as well.
After searching and trying the different ways I found I either wasn't happy with the way I was doing the code or it didn't work right for me. I'm new at programming so my understanding is limited. Please keep in mind with the answer.
I want to read a .csv file line by line and skipping lines that are blank. With the contents of the lines I want to put into a list of object. I have everything working except for the skipping line part. Also any feedback about improving any parts of my code are all welcome. I like constructive criticism.
public void CardaxCsvFileReader()
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
try
{
using (System.IO.StreamReader cardaxSR =
new System.IO.StreamReader(System.IO.File.OpenRead(cardaxCsvPath)))
{
string line = "";
string[] value = line.Split(',');
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(',');
if (line != ",,,,,") // using this as temp to skip the line because the above commented out part doesn't work.
{
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
}
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
EDITED
If you are lines are not truly blank and contain commas, you can split with RemoveEmptyEntries option and then check the column count.
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries); // <-- Remove empty columns while splitting. It has a side-effect: Any record with just a single blank column will also get discarded by the if that follows.
if (value.length < 6)
continue;
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
Another improvement feedback I have: When you catch an exception, it's a good practice to log the exception in addition to your custom error line. A custom error line might be good for say website users, but as a developer running some service you will appreciate the actual exception stack trace. It will help you debug a bug easier.
catch (Exception ex)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\".\r\n Exception: {1}", cardaxCsvPath, ex.ToString());
}
Just check if value.Length == 6, this way it'll skip lines which don't contain enough data for your columns
Use a dedicated CSV parser, such as the EasyCSV class available here*:
https://github.com/jcoehoorn/EasyCSV
public void CardaxCsvFileReader()
{
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult =
EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
}).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
}
I also recommend re-thinking how you structure this. The code below is better practice:
public IEnumerable<CardaxDataObject> ReadCardaxCsvFile(string filename)
{
//no try block at this level. Catch that in the method that calls this method
return EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
// You may want to put a try/catch inside the `Select()` projection, though.
// It would allow you continue if you fail to parse an individual record
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
});
}
Suddenly the method boils down to one statement (albeit a very long statement). Code like this is better, because it's more powerful, for three reasons: it's not limited to using just the one input file, it's not limited to only sending it's output to the one location, and it's not limited to only one way to handle errors. You'd call it like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult = ReadCardaxCsvFile(cardaxCsvPath).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
or like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
foreach (var result in ReadCardaxCsvFile(cardaxCsvPath))
{
Globals.CardaxQueryResult.Add(result);
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
I also recommend against using a Globalsclass like this. Find a more meaningful object with which you can associate this data.
* Disclaimer: I am the author of that parser
I'm new to C# and I'm trying to implement a button.visible true/false based on the contents of a txt file. Everything I've written to date is unstable at best. This is for a Winform stand alone application in the main dialog box.
In an ideal world it seems it should be simpler. I want the code to open Permissions.txt, which I know I am successfully accessing as the MessageBox will show the first name in the list, and compare the Environment.UserName with all of the names in the .txt file. Once the button is displayed it opens a new dialog box.
Anyone willing to teach a newcomer. I've been searching for a while and I don't see it.
I have also tried working with File.Readlines with no success.
Thank you in advance for any assistance you're willing to provide.
Frank Pytel
public void hideWidget()
{
//gets the users login name from the system
string newName = userNameOnly();
// Read the file and display it line by line.
System.IO.StreamReader file =
new System.IO.StreamReader(dataFolder + "\\Permissions.txt");
//This next bit called Original Code works on my local when I access it, when accessed from a server, but not for other users.
//Original code
//while ((line = file.ReadLine()) != null)
//{
// if (line == newName)
// {
// WidgetForm.Visible = true;
// }
// else
// {
// WidgetForm.Visible = false;
// }
// //MessageBox.Show(line);
// counter++;
//}
//file.Close();
//This is where I am at currently. Again it's not picking up all of the names in the .txt file.
while (file.ReadLine() != null)
{
//string line;
string line = file.ReadLine();
if (newName == file.ReadLine())
{
WidgetForm.Visible = false;
}
else
{
WidgetForm.Visible = true;
}
int counter = 0;
//MessageBox.Show(line);
//MessageBox.Show(file.ReadLine());
counter ++;
}
//file.Close();
}
EDITED....
Also if there is anyone that could possibly explain how string line; is being set to my user name. That is how it should have been set, but I've never told it line == newName in the original code. I thought that is what the While is for. To check to see if they are equal..
FINAL EDIT.
Here is what I got to work. Thanks #Bedford.
This portion goes directly below the Form1 class
string[] lines = File.ReadAllLines(dataFolder + "\\Permissions.txt");
This is the logic behind the hideWidget() button
public void hideWidget()
{
//Make all userNames available to the logic
string newName = userNameOnly();
//variable to decide if userExists is true/false
bool userExists;
//Loop through all of the userNames in the file and see if it matches the userName login
while (lines != null)
{
//Decide to make the button available if userExists does exist in the file
if (lines != null)
{
userExists = lines.Any(ln => ln == newName);
WidgetForm.Visible = userExists;
}
//Do nothing if the userName does not match anyone in the Permissions.txt file. The button default Visible is false
else
{
}
return;
}
}
I'm posting this snippet so that others might benefit from it. Thanks again #Bedford. This NEWB really appreciates the assistance. HAGD!! :-)
You can read all the lines from a file with the File.ReadAllLines static method, and then use a LINQ query to check whether any of the lines match the user name:
string[] lines = File.ReadAllLines(Path.Combine(dataFolder, "Permissions.txt"));
bool userExists = lines.Any(ln => ln == newName); // or any comparison you like
// use the bool variable to set the visibility
WidgetForm.Visible = userExists;
I found this link:
C# Launch default browser with a default search query
But with FireFox as my default browser, it tries to find a file named as whatever is in the quotes in the selected Answer there.
Code;
//ToolStripMenu Click event to launch default browser and search internet for the value in a particular cell
private void tsmSearch_Click(object sender, EventArgs e)
{
int key = mp.GetRowAt(gdcErrorLogDefaultView, rowX, rowY);
if (key < 0)
return;
string ex = gdcErrorLogDefaultView.GetRowCellValue(key, "Exception").ToString();
string name = GetDefaultBrowser();
Process.Start(name, "\"?" + ex + "\"");
}
//Gets default browser from registry
private string GetDefaultBrowser()
{
string name;
RegistryKey regKey = null;
try
{
//set the registry key we want to open
regKey = Registry.ClassesRoot.OpenSubKey("HTTP\\shell\\open\\command", false);
//get rid of the enclosing quotes
name = regKey.GetValue(null).ToString().ToLower().Replace("" + (char)34, "");
//check to see if the value ends with .exe (this way we can remove any command line arguments)
if (!name.EndsWith("exe"))
//get rid of all command line arguments (anything after the .exe must go)
name = name.Substring(0, name.LastIndexOf(".exe") + 4);
}
catch (Exception ex)
{
name = string.Format("ERROR: An exception of type: {0} occurred in method: {1} in the following module: {2}", ex.GetType(), ex.TargetSite, this.GetType());
}
finally
{
//check and see if the key is still open, if so
//then close it
if (regKey != null)
regKey.Close();
}
return name;
}
I found the GetDefaultBrowser() code somewhere on StackOverflow yesterday but I can't find the link now. The weird thing is though, I have Chrome set as my default browser but that registry key still says FireFox.
Is there a more simple way to launch the default browser and use their default search provider to look up a term?
Try this:
string searchQuery = "this is a search";
Process.Start("https://www.google.com/search?q=" + Uri.EscapeDataString(searchQuery));
Edit: Now using correct Google link