I've made a program where you can add text over textbox and it adds it into a listbox. Now to actually save it on the next time when the application launches, I decided to store it into some text.ini file.
Now that the next time the application launches (form load), using the StreamReader it reads all the text from that file and displays it into a listbox.
I have decided to implement an edit functionality (select the item from listbox -> right click -> edit), and rename the file (in case something was misspelled).
My problem is this:
If I have 1 item in the listbox and rename it, it works perfectly fine.
But if I have 2 or more items in the listbox but decide to rename only 1, it removes all text from the file and renames only the selected item.
This is my code:
string itemName = (string)lBoxRadioLinks.SelectedItem;
var renameFile = Interaction.InputBox("Enter a new name for the file:", "Rename File", itemName);
// Gets the selected item full name
var url = lBoxRadioLinks.GetItemText(lBoxRadioLinks.SelectedIndex);
string lineToRename = url.ToString();
// Selected file
//MessageBox.Show(lineToRename);
// ACTUALLY rename the text
string[] lines = File.ReadAllLines("IO/sites.ini");
List<string> list = new List<string>();
foreach (string line in lines)
{
if (line != lineToRename)
list.Add(line);
}
// Have to convert the string into array for an actual rename
string[] arr = new string[] { renameFile };
// Visually rename the file (display in listbox asap)
lBoxRadioLinks.DataSource = arr;
// Renames the file
File.WriteAllLines("IO/sites.ini", arr);
Obviously the method "WriteAllLines" will rename the whole text inside of a file, but I don't want that.
How can I rename a text that is at a certain line, without effecting other lines in a file?
I don't really know what you're asking, but I'll give this my best shot.
If we're understanding each other when we agree that you want to replace certain text in a text file, and if you're actually okay loading the whole thing into memory, then you might as well just use the string.Replace method.
Dictionary<string, string> changesToMake = ...;
var contents = File.ReadAllText(filename);
foreach(var change in changesToMake)
{
contents = contents.Replace(change.Key, change.Value);
}
File.WriteAllText(filename, contents);
As for the problem you're having with multiple items in the ListBox, you're only referencing the SelectedItem and SelectedIndex properties, so nothing should get changed anywhere other than at those...files? I'm still not even sure of what's in that ListBox. If you want to alter more than one at a time, you should be using the pluralized forms of each of those properties to access all of the selected items and not just the first.
Related
How do I make some spacing between some items in my listView? I have a program, that lists all files in their respective folders, but now the list looks like:
---FolderName1---
fileName1
fileName2
fileName3
---FolderName2---
fileName1
fileName2
And so on...
The view mode I am using is List, and I would like to have some spacing where the folder names are, but I would like to avoid having to add another empty line, just to have a bit of space between the last file name and the folder name, and the first file. How do I achieve that?
for (int i = 0; i < folderNum; i++)
{
listView1.Items.Add(folderNum[i].Name);
for (int j = 0; j < fileNum; j++)
{
listView1.Items.Add(fileNum[j].Name);
}
}
You can use View=Details and create Groups. They are taller but unfortunately I found no way to set their Font etc..
You can add the Groups while you add the Items or before or later. You can add more Groupsor change to which an item belongs at any time. You can also remove a Group; in that case all its Items get move to the 'default' Group.
Here is an example:
Prepare a few data:
string root = #"D:\scrape\patterns";
string mask = "*10*";
List<string> folders = Directory.GetDirectories(root).ToList();
List<string> files = Directory.GetFiles(root, mask, SearchOption.AllDirectories).ToList();
Prepare the ListView:
listView1.View = View.Details;
listView1.Columns.Add("Png Files");
Now add Groups:
// 1st group: add root search folder..:
listView1.Groups.Add(root,root);
// .. and all 1st level nested folders as more (possible) groups:
foreach (string f in folders) listView1.Groups.Add(f, f);
Finally insert Items:
// insert items with groups:
foreach (string f in files)
{
string key = Path.GetDirectoryName(f);
string text = Path.GetFileName(f);
var lvi = listView1.Items.Add(text);
lvi.Group = listView1.Groups[key];
}
Note that you can change the Group an Item belongs to at any time..:
listView2.Items[2].Group = listView2.Groups[1];
A few notes:
If you look closely you can see that the Item I moved was assigned to a Group which was empty before. Those are not displayed at all.
Any Item without a Group is added to a Group "default", which is displayed at the top. You can't change the name, so it is best to create one of your own and assign 'ungrouped' items to it. I used the name of the search root for this.
I know of no way to style the groups beyond HeaderText and HeaderAlignment; there seem to be no properties nor are they drawn in any of the DrawXXX events. There are strangley incomplete hints at doing it in an DrawVisualStyleElementListViewGroup1 event or function but I couldn't find out how to use it..
I have a drop down combo box that changes the image of a picture box. The images are stored in a resx file with an array that counts them, so that if I ever decide to add more, I need only to update the combo box, and add the images to the resx file. The problem I'm having is that when I update the image with the combo box, the images in the resx aren't in alphabetical order, but they do change the image on the picture box.
Here is my code
ResourceSet cardResourceSet = Properties.Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
attributes = new Image[cardCount];
cardCount = 0;
foreach (DictionaryEntry entry in cardResourceSet)
{
string resourceKey = (string)entry.Key;
object resource = entry.Value;
cardCount++;
}
attributes = new Image[cardCount];
cardCount = 0;
foreach (DictionaryEntry entry in cardResourceSet)
{
attributes[cardCount] = (Image)entry.Value;
cardCount++;
}
if (attributeBox.SelectedIndex != -1)
{
this.cardImage.Image = attributes[attributeBox.SelectedIndex];
}
How can I go about making it sort the resources in the resx alphabetically?
The return type of GetResourceSet, ResourceSet, implements IEnumerable, so you should be able to run some LINQ ordering on it:
foreach (var entry in cardResourceSet.Cast<DictionaryEntry>().OrderBy(de => de.Key))
{
}
Since you're iterating over it twice (though I'm not really sure of the point of the first for-loop, unless there's other code), you may want to assign the sorted results to a separate variable:
var sortedCardResourceSet.Cast<DictionaryEntry>().OrderBy(de => de.Key).ToList();
foreach (var entry in sortedCardResourceSet)
{
...
}
...
I have a wpf c# application that gets the name of household chores from a text file and then loads then into a several lists such as a list of nameOfChore, a list of ChorePriority, and then load the lists into the treeview, when i select one of the tasks/chores in the treeView I am trying to get it to load the list data for the given chore into text boxes on the right. I have a for loop checking if the treeView selected item is equal to any of the names of chores, and if it is, it then loads the text box. The problem is with the selectName variable it should be loaded with the chore name such as "Cut Grass" but instead it gets loaded with "System.Windows.Controls.TreeViewItem Header:Cut Grass Items.Count:4" how can i get selectName to just equal the header of the selected treeView item. Thanks.
for (int i = 0; i < name.Count; ++i) {
//string selectName = treeView1.SelectedItem.ToString(); I have tried this, and it gets the same value
string selectName = treeView1.SelectedValue.ToString();
if (selectName == name[i])
{
in treeView_selectedItemChanged event handler use following code works fine for me:
string selectName = ((TreeViewItem)(treeView1.SelectedItem)).Header.ToString();
hope that's what you needed!
Hi I have a dictionary object implemented:
public partial class Window1 : Window
{
...
public Dictionary<string, string> Dictionarycheck = new Dictionary<string, string>();
public Window1()
{
Dictionarycheck.Add("Execbuildstartingmail", "Execbuildstartingmail");
Dictionarycheck.Add("Execbuildlastmail", "Execbuildlastmail");
...
}
}
then I have xml file to be parsed. I have more than 1 of such target to be parsed. I am supposed to parse the string of the attribute "if" so over here i would need "Execexample" as a string.
<project>
...
<target if="Execexample">
...
</target>
...
</project>
To do this parsing i tried to implement this code (which i'm not sure if it is correct):
private void xmlparsingButton_Click(object sender, RoutedEventArgs e)
{
XDocument xmlDoc = XDocument.Load(#"C:\Build.xml");
var abc = from target in xmlDoc.Descendants("target")
select (string)target.Attribute("if");
foreach(string target in abc)
{
// check this value whether if it exist in the dictionary.
// if not create a checkbox for it.
}
}
For the "foreach" statement i am supposed to do a check with the dictionary object. If there is no such value in dictionary, i will create a checkbox in my tabitem 5 and the name as the string (for example ExecexamplecheckBox).
how should i implement such a foreach function?
EDIT 1:
I forgot to add something. I need to write a text to a text file if these new checkboxes are checked. How do i do that? And they are required to write to a textfile that was written previously.
Does this solve the problem?
foreach(string target in abc)
{
//check this value whether if it exist in the dictionary. if not create a checkbox for it.
if (!Dictionarycheck.ContainsKey(target))
{
CheckBox chk = new CheckBox();
chk.Text = target;
SomePanel.Controls.Add(chk);
}
}
I can see two separate problems here:
1 - Data problem
Parse the xml file and extract the necessary data to a seperate list or dictionary.
2 - User interface
Generate a series of checkboxes based on the output of the data problem.
First you should separate the data problem away from the UI. In the foreach loop build a list of objects. The object should have at least a string property, for display, and a boolean property for the checkbox to bind to.
Second, on your UI create a ItemsControl (such as a ListBox) and bind its ItemsSource to the above list, then set the ItemTemplate to a DataTemplate containing a CheckBox
<ListBox ItemsSource="{Binding Path=SomeList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=DisplayString}" IsChecked="{Binding Path=Selected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After the user has finished checking/unchecking the list, you will be left with a list upon which to write your text lines to a file.
There are several concepts you should learn when dealing with WPF, probably the hardest to get your head around is the seperation of concern between the UI and the data, and how the data layer can feed the UI to create elements for you. There should be no need to create new checkboxes in code using this technique.
Also, you will be better off not binding directly to a dictionary (although it is possible) but you should use ObservableCollection as your ItemsSource, it will handle change notification to the UI for you.
With the looks of it, as #Jackie mentioned, you could use HashSet. It won't make a difference except that it is the correct used of the right data structure in this case.
var filePath = "PathAndFileNameOfSomeFile.txt";
var hashset = new HashSet<string> { { "Execbuildstartingmail" }, { "Execbuildlastmail" } };
var xmlString = "<project><target if=\"Execexample\"></target><target if=\"Execbuildlastmail\"></target></project>";
var xDocument = XDocument.Parse(xmlString);
var attributeValues = from attrs in xDocument.Descendants("target").Attributes("if")
select attrs.Value;
foreach (var value in attributeValues)
{
var controls = this.Controls.Find(value, true);
//No checkbox with matching name found
if (controls.Length == 0)
{
if (!hashset.Contains(value))
{
var checkbox = new CheckBox();
checkbox.Parent = tabControl1.TabPages["tabPage2"];
checkbox.Text = value;
}
}
//checkbox with the given name was found AND is checked
else if (controls.Length == 1 && ((CheckBox)controls[0]).Checked)
{
using (var writer = File.AppendText(filePath))
{
writer.WriteLine(value);
}
}
}
After your Edit
I'm not clear about what you're saying in your edit, so I'll rephrase it...
While inthe foreach, if the checkbox exists and is checked, then you need to write some test to a text file. If they are not check there is nothing to do. If a checkbox does not exist, then add it to the tab is that correct?
I've updated the code to show you how to do three things:
Find Checkboxes in your form.
See if a checkbox is checked or not
Write some text to a text file.
even of the code I've provided does not do "Exactly" what you want I believe the various parts should help you get to your end goal. If not then please simply your question and provide very clear statements of where specifically you need help.
I have an Array List to save selected files and a ListBox to display only the name of the files..my requirement is to delete corresponding files from arraylist when its deleted from listbox...here is my code:
public ArrayList to_compress = new ArrayList();
ListBox pack_lbx=new ListBox();
private void add_btn_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
foreach (string f in ofd.FileNames)
{
FileInfo f_inf = new FileInfo(f);
if (pack_lbx.Items.IndexOf(Path.GetFileName(f)) == -1)
{
to_compress.Add(new string[] { f, f_inf.Name });
pack_lbx.Items.Add(Path.GetFileName(f));
}
}
}
private void remove_btn_Click(object sender, EventArgs e)
{
// pack_lbx.Items.Remove(pack_lbx.Items);
ListBox.SelectedObjectCollection s = pack_lbx.SelectedItems;
while (s.Count > 0)
{
pack_lbx.Items.Remove(s[0]);
to_compress.Remove(s.ToString()); //this doesnt work
}
}
I don't see a question here. I'm assuming that you're getting an error because you're trying to modify a collection that you're actively looping through?
If that's NOT the issue, please change this to a question so that we can give a better answer.
However, assuming I am guessing right...
You can't do that... It messes up things if you alter the list you're looping through.
Instead, you should be creating a NEW list and adding items to it (copying them from the list you're looping through as you're looping through it) and just skipping the "add" code for the items you want to "delete".
while (s.Count > 0)
{
pack_lbx.Items.Remove(s[0]);
to_compress.Remove(s.ToString());//this doesnt work
}
this will not work because , you are deleting object from a collection while looping through the collection so
do this
private void remove_btn_Click(object sender, EventArgs e)
{
// pack_lbx.Items.Remove(pack_lbx.Items);
ArrayList tempList = new ArrayList();
ListBox.SelectedObjectCollection s = pack_lbx.SelectedItems;
foreach(string str in to_compress)
{
if(!s.Contains(str))
tempList.Add(str)
}
to_compress = tempList;
}
Trying to keep two identical lists synchronised is a pattern that opens you up to bugs, because if you fail to synchronise correctly in just one place, your UI will be displaying information that is different from what your program is using internally.
A better approach is to only keep one "master" list. Let the ListBox hold the list, manipulate it in the ListBox, and only copy the filenames out of the ListBox at the end of the process.
If you want the text displayed in the listbox to be different from the underlying string (e.g. display leafnames in the box but keep full pathnames internally) then you can create a trivial class to hold the full pathname and override its ToString() to return the leafname. Then add instances of this class rather than raw strings to the ListBox.
If you insist on keeping two lists in sync, then the easiest is to use ListBox.SelectedIndex with the RemoveAt() method, and simply remove the same item from both lists.
If you need to remove an item from a list that you are enumerating, you can either:
Use a for loop with an index instead of foreach to do the iterating. You can then acess list items with the array index [i] syntax, and delete them with RemoveAt(i). Just be careful with how you advance the index after deleting an item.
Use a separate variable/list to store references to the items you wish to delete during your first loop, and then execute a second loop over this list to actually do the deletion as a post-processing step.
to_compress.Remove(s[0].ToString());
I can't see any line of code that add the files to the ArrayList to_compress.
Also if you adding the FileInfo object to arraylist you can't remove it using the name of the file.
Consider using generic dictionary that have name of file as key and the actual file object as value.
var to_compress = new Dictionary<string,FileInfo>();
to_compress.Add(filename,File);
//then you can remove by
to_compress.Remove(filename);
//you can loop through it like so
foreach (var pair in to_compress)
{
string filename = pair.Key;
FileInfo file = pair.Value;
}