Saving windows form listbox to a text file C# - c#

I'm trying to save the contents of a listbox into a text file. and it works, but instead of the text entered into the list box, I get this:
System.Windows.Forms.ListBox+ObjectCollection
Here is the relevant code I'm using for the form itself.
listString noted = new listString();
noted.newItem = textBox2.Text;
listBox1.Items.Add(textBox2.Text);
var radioOne = radioButton1.Checked;
var radioTwo = radioButton2.Checked;
var radioThree = radioButton3.Checked;
if (radioButton1.Checked == true)
{
using (StreamWriter sw = new StreamWriter("C:\\windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else if (radioButton2.Checked == true)
{
using (StreamWriter sw = new StreamWriter("C:\\Users\\windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else if (radioButton3.Checked == true)
{
using (StreamWriter sw = new StreamWriter("../../../../windowsNotes.txt"))
{
sw.Write(listBox1.Items);
}
}
else
{
MessageBox.Show("Please select a file path.");
}
}
The class is just a simple one:
namespace Decisions
{
public class listString
{
public string newItem {get; set;}
public override string ToString()
{
return string.Format("{0}", this.newItem);
}
}
}

You cannot just do
sw.Write(listBox1.Items);
as it's calling .ToString() on the collection object itself.
Try something like:
sw.Write(String.Join(Environment.NewLine, listBox1.Items));
Or loop through each item and ToString the individual item.

You will have to write the items one by one:
using (StreamWriter sw = new StreamWriter("C:\\windowsNotes.txt") {
foreach (var item in listBox1.Items) {
sw.WriteLine(item.ToString());
}
}

You're writing the ToString of the collection to the output stream rather than of the elements of the collection. Iterating over the collection and outputting each one individually would work, and I'm sure there there's a succint Linq (or even more obvious) way of doing that.

Related

Iterating through a list populated by a text file to look for a reference C#

I'm new to C# and i'm trying to check whether a guest has checked in on a hotel app. I'm trying to get all bookings in a text file pass them in to a list then read through the list looking for the booking in reference.
The problem i'm having is that it only seems to put the first line of the text file into the list. Could anyone help me solve this?
One way i've tried:
public void CheckBookingReference()
{
List<string> BookingList = File.ReadAllLines(BookingFilePath).ToList();
foreach (var BookingLine in BookingList.ToList())
{
string[] bookings = BookingLine.Split(',');
int _BookingReferenceNumber = int.Parse(bookings[5]);
if (_BookingReferenceNumber == BookingReferenceNumber)
{
Console.WriteLine("Booking found, check in complete.");
break;
}
else
{
throw new Exception("BookingNotFoundException");
}
}
}
Another way i've also tried:
public void CheckBookingReference()
{
List<string> BookingList = new List<string>();
using (var sr = new StreamReader(BookingFilePath))
{
while (sr.Peek() >= 0)
BookingList.Add(sr.ReadLine());
foreach (var BookingLine in BookingList.ToList())
{
string[] bookings = BookingLine.Split(',');
int _BookingReferenceNumber = int.Parse(bookings[5]);
if (_BookingReferenceNumber == BookingReferenceNumber)
{
//throw new Exception("GuestAlreadyCheckedInException");
Console.WriteLine("booking found");
break;
}
else if (_BookingReferenceNumber != BookingReferenceNumber)
{
Console.WriteLine("not found");
break;
}
}
}
}
With the code you have, if the first line doesn't match, you throw the exception.
Try this:
public void CheckBookingReference()
{
List<string> BookingList = File.ReadAllLines(BookingFilePath).ToList();
foreach (var BookingLine in BookingList.ToList())
{
string[] bookings = BookingLine.Split(',');
int _BookingReferenceNumber = int.Parse(bookings[5]);
if (_BookingReferenceNumber == BookingReferenceNumber)
{
Console.WriteLine("Booking found, check in complete.");
return;
}
}
throw new Exception("BookingNotFoundException");
}

searching for strings in a text file using foreach loops

so I am trying to search a file for some specific strings these strings are stroed in a list and are called universities, Courses and UGPG I am using a Streamreader to load the file in.
the issue I am having is that after the first foreach loop has executed the remaining searches I want to complete return N/a as if the strings are not present in the text file. however I know they are in the text file.
Is there a reason for this or a better way to code this?
my code is below.
any help would be greatly appreciated.
validdirectory = new DirectoryInfo(path);
Vfiles = validdirectory.GetFiles("*.txt");
foreach (FileInfo file in Vfiles)
{
//reads the file contents
bool Stepout = false;
bool nextouterloop = false;
using (StreamReader ReadMessage = new StreamReader(file.FullName))
{
String MessageContents = ReadMessage.ReadToEnd();
Message_Viewer.Text = MessageContents;
foreach (string Uni_Name in Universities)
{
if (MessageContents.Contains(Uni_Name))
{
Display_Uni.Text = Uni_Name;
}
}
foreach (string course in Courses)
{
if (MessageContents.Contains(course))
{
Display_Course.Text = course;
}
Display_Course.Text = "N/A";
}
if (MessageContents.Contains("Postgraduate"))
{
Display_UGPG.Text = "Postgraduate";
}
else if (MessageContents.Contains("Undergraduate"))
{
Display_UGPG.Text = "Undergraduate";
}
Display_UGPG.Text = "N/A";
}
}
Remove the assignement of N/A inside the loop and let it run until completition.
At the end you could just test the content of the textboxes to see if your loops have found something and, if not, set the N/A text
foreach (string course in Courses)
{
if (MessageContents.Contains(course))
Display_Course.Text = course;
}
if (MessageContents.Contains("Postgraduate"))
Display_UGPG.Text = "Postgraduate";
else if (MessageContents.Contains("Undergraduate"))
Display_UGPG.Text = "Undergraduate";
if(string.IsNullOrWhitespace(Display_Course.Text))
Display_Course.Text = "N/A";
if(string.IsNullOrWhitespace(Display_UGPG.Text ))
Display_UGPG.Text = "N/A";
By the way, having you used arrays or lists for the universities and courses I suppose that you want to see all the matching names. Actually, your code writes always the last course and university found in the textboxes overwriting the previous name found.
You should change the line that set the Text property with a call to AppendText (perhaps adding also a newline if the textboxes are multiline = true)
....
Display_Uni.AppendText(Uni_Name + Environment.NewLine);
...
Display_Course.AppendText(course + Environment.NewLine);
Here is a possible solution without the complicated foreach loops:
if (Universities.Select(p => MessageContents.Contains(p)).Any())
{
Display_Uni.Text = Uni_Name;
}
else if (Courses.Select(p => MessageContents.Contains(p)).Any())
{
Display_Course.Text = course;
}
else if (MessageContents.Contains("Postgraduate"))
{
Display_UGPG.Text = "Postgraduate";
}
else if (MessageContents.Contains("Undergraduate"))
{
Display_UGPG.Text = "Undergraduate";
}
else
{
Display_UGPG.Text = "N/A";
}

How can I pass a function with one parameter to an ICommand?

Here's my ICommand:
public ICommand ConfirmLotSavedCommand {
get
{
return new RelayCommand(ConfirmLotSaved);
}
}
The problem is I have deserialized data that I want to store into database after a user clicks confirm button. If the user does not click on confirm or the lot number already exists, then I don't want to save the deserialized string in db.
I had trouble calling a function with one parameter inside my ConfirmLotSaved() method because of scope.
So I created a set the deserialized lot as a field and put the code to save to db inside of ConfirmLotSaved(). However, the field is null for some strange reason... I'm not sure why.
Here's my attempt:
private LotInformation lot; //field that is supposed to contain all the deserialized info
private void ConfirmLotSaved()
{
using (var db = new DDataContext())
{
bool lotNumDbExists = db.LotInformation.Any(r => r.lot_number == DeserialLotNumber);
if (lotNumDbExists == false)
{
successWindow.Message = "Successfully Saved Lot";
dialogService.ShowDialog(successWindow.Message, successWindow);
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = Lot.lot_number;
ExpirationDate = Lot.exp_date.ToString();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
else if (lotNumDbExists == true)
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
}
Deserialization function to see where lot is grabbing data:
public void DeserializedStream(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
LotInformation lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
LotInformation newList = new LotInformation();
using (var db = new DDataContext())
{
bool isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (newList != null && isDuplicate == false)
{
newList.Id = lot.Id;
newList.lot_number = lot.lot_number;
newList.exp_date = lot.exp_date;
DeserialLotNumber = newList.lot_number;
DeserialExpirationDate = newList.exp_date.ToString();
foreach (Component comp in lot.Components)
{
newList.Components.Add(comp);
}
DeserialComponentsList = newList.Components;
foreach (Families fam in lot.Families)
{
newList.Families.Add(fam);
}
DeserialFamiliesList = newList.Families;
}
else if (isDuplicate == true)
{
DeserialAnalytesList = null;
DeserialFamiliesList = null;
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
I figured out what was wrong:
After setting private LotInformation lot; field before constructor, I redeclared locally my mistake:
LotInformation lot = new LotInformation();
Changed it to:
lot = new LotInformation();
and it works.
I suggest you to use RelayCommand's generic edition http://www.kellydun.com/wpf-relaycommand-with-parameter/
It will allow you to pass lot to your command from view, all you need to store lot in current DataContext.

Programmatically add multiple categories to Outlook 2010

I am trying to create multiple categories in Outlook 2010 using C#. I am able to successfully generate an executable that will create one category but when I add code to create a second category it will still only add the first one and not the second. If The first category exists it will then add the second one but it will not create both from scratch at the same time.
Below is my code. Any help is greatly appreciated.
using System;
using System.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Outlook;
namespace OutlookCategory
{
class Program
{
static void Main(string[] args)
{
AddACategory();
}
private static void AddACategory()
{
var app = new Application();
Outlook.Categories categories = app.Session.Categories;
if (!CategoryExists("TEST 1", app))
{
categories.Add("TEST 1", Outlook.OlCategoryColor.olCategoryColorDarkBlue);
}
if (!CategoryExists("TEST 2", app))
{
categories.Add("TEST 2", Outlook.OlCategoryColor.olCategoryColorDarkBlue);
}
}
private static bool CategoryExists(string categoryName, Application app)
{
try
{
Outlook.Category category =
app.Session.Categories[categoryName];
if (category != null)
{
return true;
}
else
{
return false;
}
}
catch { return false; }
}
}
}
I ended up accomplishing this by creating a pipe delimited text file with my category names and their associated category color.
I then looped through the text file creating all of the categories in Outlook.
using System;
using System.IO;
using Microsoft.Office.Interop.Outlook;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace OutlookCategory
{
class Program
{
static void Main(string[] args)
{
AddACategory();
}
private static void AddACategory()
{
string[] lines = File.ReadAllLines(#"C:\CategoryList.txt");
var app = new Application();
foreach (string line in lines)
{
string[] LineArray = line.Split('|');
var color = LineArray[1];
Outlook.Categories categories = app.Session.Categories;
//Add categories
if (CategoryExists(LineArray[0], app) == false)
{
categories.Add(LineArray[0], color);
}
}
}
private static bool CategoryExists(string categoryName, Application app)
{
try
{
Outlook.Category category =
app.Session.Categories[categoryName];
if (category != null)
{
return true;
}
else
{
return false;
}
}
catch { return false; }
}
}
}
The answer already provided did not do the trick for me. I would still get spotty results while adding and removing categories. i was able to get around this by trying the add/remove, then re-getting the categories and looping until the item was added/removed as needed.
Here is code snipped from LinqPad where I would remove all of the categories, add back in my own, then list some categories and their values. This has worked with no problems since i switched to the while/loop check. I tried a few different sleep times and it didn't seem to make much of a difference. I am guessing outlook must need to do something before the new category actually 'goes in' to the datastore. Regardless, I didn't want to spend a ton of time on this, and this worked for me. I am using outlook 2010 on windows 7. not sure if newer versions of office have this same issue.
void Main()
{
//using Microsoft.Office.Interop.Outlook
Application oApp = new Application();
NameSpace oSes = oApp.Session;
Categories oCats = oSes.DefaultStore.Categories;
//remove existing categories
var catids = (from a in oCats.Cast<Category>() select a.CategoryID).ToArray();
for (int i = 0; i < catids.Count(); i++)
{
var cid = catids[i];
cid.Dump();
while(oCats[cid] != null)
{
oCats.Remove(cid);
oCats = oSes.DefaultStore.Categories;
Thread.Sleep(100);
}
}
//dictionary to hold my categories
Dictionary<string,OlCategoryColor> dCats = new Dictionary<string,OlCategoryColor>();
dCats.Add("Category One",OlCategoryColor.olCategoryColorRed);
dCats.Add("Category Two",OlCategoryColor.olCategoryColorOrange);
dCats.Add("Category Three",OlCategoryColor.olCategoryColorPeach);
dCats.Add("Category Four",OlCategoryColor.olCategoryColorYellow);
foreach (var dCat in dCats)
{
var cid = dCat.Key;
cid.Dump();
while(oCats[cid] == null)
{
oCats.Add(cid,dCat.Value);
oCats = oSes.DefaultStore.Categories;
Thread.Sleep(100);
}
}
//show categories
var cats = from c in oCats.Cast<Category>() select new {
c.CategoryBorderColor,
c.CategoryGradientBottomColor,
c.CategoryGradientTopColor,
c.CategoryID,
c.Class,
c.Color,
c.Name,
c.ShortcutKey
};
cats.Dump();
}

Retrieving Data From XML File

I seem to be having a problem with retrieving XML values with C#, which I know it is due to my very limited knowledge of C# and .XML.
I was given the following XML file
<PowerBuilderRunTimes>
<PowerBuilderRunTime>
<Version>12</Version>
<Files>
<File>EasySoap110.dll</File>
<File>exPat110.dll</File>
<File>pbacc110.dll</File>
</File>
</PowerBuilderRunTime>
</PowerBuilderRunTimes>
I am to process the XML file and make sure that each of the files in the exist in the folder (that's the easy part). It's the processing of the XML file that I have having a hard time with. Here is what I have done thus far:
var runtimeXml = File.ReadAllText(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
var doc = XDocument.Parse(runtimeXml);
var topElement = doc.Element("PowerBuilderRunTimes");
var elements = topElement.Elements("PowerBuilderRunTime");
foreach (XElement section in elements)
{
//pbVersion is grabbed earlier. It is the version of PowerBuilder
if( section.Element("Version").Value.Equals(string.Format("{0}", pbVersion ) ) )
{
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
}
}
My issue is that the String List is only ever populated with one value, "EasySoap110.dll", and everything else is ignored. Can someone please help me, as I am at a loss.
Look at this bit:
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
You're iterating over each Files element, and then finding the first File element within it. There's only one Files element - you need to be iterating over the File elements within that.
However, there are definitely better ways of doing this. For example:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var fileList = (from runtime in doc.Root.Elements("PowerBuilderRunTime")
where (int) runtime.Element("Version") == pbVersion
from file in runtime.Element("Files").Elements("File")
select file.Value)
.ToList();
Note that if there are multiple matching PowerBuilderRunTime elements, that will create a list with all the files of all those elements. That may not be what you want. For example, you might want:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var runtime = doc.Root
.Elements("PowerBuilderRunTime")
.Where(r => (int) r.Element("Version") == pbVersion)
.Single();
var fileList = runtime.Element("Files")
.Elements("File")
.Select(x => x.Value)
.ToList();
That will validate that there's exactly one matching runtime.
The problem is, there's only one element in your XML, with multiple children. You foreach loop only executes once, for the single element, not for its children.
Do something like this:
var fileSet = files.Elements("File");
foreach (var file in fileSet) {
fileList.Add(file.Value);
}
which loops over all children elements.
I always preferred using readers for reading homegrown XML config files. If you're only doing this once it's probably over kill, but readers are faster and cheaper.
public static class PowerBuilderConfigParser
{
public static IList<PowerBuilderConfig> ReadConfigFile(String path)
{
IList<PowerBuilderConfig> configs = new List<PowerBuilderConfig>();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
XmlReader reader = XmlReader.Create(stream);
reader.ReadToDescendant("PowerBuilderRunTime");
do
{
PowerBuilderConfig config = new PowerBuilderConfig();
ReadVersionNumber(config, reader);
ReadFiles(config, reader);
configs.Add(config);
reader.ReadToNextSibling("PowerBuilderRunTime");
} while (reader.ReadToNextSibling("PowerBuilderRunTime"));
}
return configs;
}
private static void ReadVersionNumber(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToDescendant("Version");
string version = reader.ReadString();
Int32 versionNumber;
if (Int32.TryParse(version, out versionNumber))
{
config.Version = versionNumber;
}
}
private static void ReadFiles(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToNextSibling("Files");
reader.ReadToDescendant("File");
do
{
string file = reader.ReadString();
if (!string.IsNullOrEmpty(file))
{
config.AddConfigFile(file);
}
} while (reader.ReadToNextSibling("File"));
}
}
public class PowerBuilderConfig
{
private Int32 _version;
private readonly IList<String> _files;
public PowerBuilderConfig()
{
_files = new List<string>();
}
public Int32 Version
{
get { return _version; }
set { _version = value; }
}
public ReadOnlyCollection<String> Files
{
get { return new ReadOnlyCollection<String>(_files); }
}
public void AddConfigFile(String fileName)
{
_files.Add(fileName);
}
}
Another way is to use a XmlSerializer.
[Serializable]
[XmlRoot]
public class PowerBuilderRunTime
{
[XmlElement]
public string Version {get;set;}
[XmlArrayItem("File")]
public string[] Files {get;set;}
public static PowerBuilderRunTime[] Load(string fileName)
{
PowerBuilderRunTime[] runtimes;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var reader = new XmlTextReader(fs);
runtimes = (PowerBuilderRunTime[])new XmlSerializer(typeof(PowerBuilderRunTime[])).Deserialize(reader);
}
return runtimes;
}
}
You can get all the runtimes strongly typed, and use each PowerBuilderRunTime's Files property to loop through all the string file names.
var runtimes = PowerBuilderRunTime.Load(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
You should try replacing this stuff with a simple XPath query.
string configPath;
System.Xml.XPath.XPathDocument xpd = new System.Xml.XPath.XPathDocument(cofigPath);
System.Xml.XPath.XPathNavigator xpn = xpd.CreateNavigator();
System.Xml.XPath.XPathExpression exp = xpn.Compile(#"/PowerBuilderRunTimes/PwerBuilderRunTime/Files//File");
System.Xml.XPath.XPathNodeIterator iterator = xpn.Select(exp);
while (iterator.MoveNext())
{
System.Xml.XPath.XPathNavigator nav2 = iterator.Current.Clone();
//access value with nav2.value
}

Categories