C# listView check for repeated "fileName" entries - c#

I am new to C#
I just want to ask if is it possible to check against a SUBItem in a listview for repeated values before i do a ADD() method??
Lets say i have a listview and i can add/delete items
I can have numerous items and subitems
I would like to perform a check before adding the file that i'm opening into the listview
The file that I'm going to put into would be, name of a file, eg example.txt
And if this file exists in the subitem, i will not add into the listview
Does anyone have any idea on how to check a subitem value against the value that I'm going to add into?
TIA

Well, you can iterate through the Items property of the ListView, and then the Subitems property for each item and finally check against the subitem's Text property.
The other option is to store the already added items in a List, and check if it already contains that item you want to add.
Edit: as requested, added sample code below.
private bool _CheckFileName(string fileName)
{
foreach(ListViewItem item in this.myListView.Items)
{
// this is the code when all your subitems are file names - if an item contains only one subitem which is a filename,
// then you can just against that subitem, which is better in terms of performance
foreach(ListViewItem.ListViewSubItem subItem in item.SubItems)
{
// you might want to ignore the letter case
if(String.Equals(fileName, subItem.Text))
{
return false;
}
}
}
return true;
}
using(var ofd = new OpenFileDialog())
{
// ... setup the dialog ...
if(ofd.ShowDialog() == DialogResult.Cancel)
{
// cancel
return;
}
// note that FileOpenDialog.FileName will give you the absolute path of the file; if you want only the file name, you should use Path.GetFileName()
if(!_CheckFileName(ofd.FileName))
{
// file already added
return;
}
// we're cool...
}
I didn't test the code so it is possible that I have some typos, if so, please add a comment and I'll fix it (though perhaps it would be better if you tried to figure it out yourself first :)).

You could create a helper class that contains information about each item. For each ListViewItem you create a new instance of this class and set ListViewItem.Tag to this instance.
You'd only have to iterate over all the items, get the helper object for the item and compare with that helper object.

Related

I need my program to read a list and see that the selected item hasn't already been added

So I am drawing a blank on how to make the program read the list and see that the item has not already been added. Here is my code, this was my first idea but I am not sure what to write so that the user cannot add the select item twice.
For example in lstAvailable there is an option named Floormats, the user can add Floormats to lstSelected but if they try to after that I need the message box to say item already selected
// CREATE LIST FOR AVAILABLE OPTION PRICES (parallel to listbox)
List<double> dblAvailableItems = new List<double> { 775.18, 567.99, 312.03, 1934.99, 609.82, 1251.04, 190.08, 82.00, 600.00 };
// CREATE LIST FOR SELECTED OPTION PRICES (parallel to listbox - empty to start)
List<double> dblOrder = new List<double>();
public frmPriceCar()
{
InitializeComponent();
}
private void lstAvailableOptions_DoubleClick(object sender, EventArgs e)
{
// DETERMINE IF OPTION HAS NOT ALREADY BEEN SELECTED
if (lstAvailableOptions.SelectedIndex.ToString() == lstSelectedOptions.Items.ToString())
{
MessageBox.Show("Item already added");
return;
}
if (lstAvailableOptions.SelectedIndex > -1)
{
//ADD OPTION TO SELECTED OPTIONS LISTBOX
dblOrder.Add(dblAvailableItems[lstAvailableOptions.SelectedIndex]);
//ADD OPTION PRICE TO SELECTED OPTIONS LIST
lstSelectedOptions.Items.Add(lstAvailableOptions.Items[lstAvailableOptions.SelectedIndex]);
//CALL CALCULATE METHOD TO CALCULATE TOTALS AND DISPLY TO SCREEN
CalcTotal();
}
}
```
Instead of checking in the UI (lstAvailableOptions), just check in the list itself (dblOrder):
if (dblOrder.Contains(lstAvailableOptions.SelectedItem))
{
MessageBox.Show("Item already added");
return;
}
Also from a UX standpoint, why do you need to bother the user with a pop-up if he adds something twice? Just silently do the right thing in your code (make sure that it's only added once):
if (dblOrder.Contains(lstAvailableOptions.SelectedItem)) return;
And you can even go further than that. If instead of:
List<double> dblOrder = new List<double>();
you use:
HashSet<double> dblOrder = new HashSet<double>();
you can completely skip checking if it's already in the list. A HashSet will not add it twice.... it automatically makes sure that there's only one of each item.
This line
lstAvailableOptions.SelectedIndex.ToString() == lstSelectedOptions.Items.ToString()
compares an index to a collection, that doesn't make sense. If you step through it with a debugger you'll probably see that you are comparing e.g.
"1" == "System.Collections.Generic.List`1[System.Double]"
and that comparison of course always returns false.
Check instead if lstSelectedOptions contains the selected item, or directly if dblOrder contains the selected item:
if (dblOrder.Contains(dblAvailableItems[lstAvailableOptions.SelectedIndex])
{
MessageBox.Show("Item already added");
return;
}

Read assembly MethodBody, parse its Name in a ListBox and its IL in a TextBox?

I have a WPF MainWindow Form with a ListBox and a TextBox that looks like this:
Figure A. WPF MainWindow with Sample Text.
Now, the Load Assembly... OnClick button event allows me to select a .NET Assembly and load it up using DnLib
Then, if I want to display the Methods bodies I would do it like so:
Assembly asm = Assembly.LoadFile(filename);
foreach (Module mod in asm.GetModules())
{
foreach (Type types in mod.GetTypes())
{
foreach (MethodInfo mdInfo in types.GetMethods())
{
listBox.Items.Add(mdInfo.Name);
}
}
}
This adds each found Method name to the ListBox on the left, resulting like so:
Figure B. Showing the ListBox Filled with Methods Names
Now the trick part, I would like to for whichever method I select from the ListBox to display its respective MethodBody IL on the TextBox
How can I achieve such thing?
«Phew!» Finally Solved it!
Here's the solution for whoever tries to do the same thing in the future.
Make an instance of 'List' and then iterate through the methods and assign the names to such list, then whenever your SelectedItem index value changes, I can simply call GetMethodBodyByName and then I can surely solve this issue
Here's how to implement the function GetMethodBodyByName:
public string GetMethodBodyByName(string methodName)
{
ModuleDefMD md = ModuleDefMD.Load(filename);
foreach (TypeDef type in md.Types)
{
foreach (MethodDef method in type.Methods)
{
for (int i = 0; i < type.Methods.Count; i++)
{
if (method.HasBody)
{
if (method.Name == methodName)
{
var instr = method.Body.Instructions;
return String.Join("\r\n", instr);
}
}
}
}
}
return "";
}
The idea is that 'GetMethodBodyByName' will receive the method name as a parameter, then it will iterate through methods and see if a method matches the given name, then if found, the function will just simply iterate through that method and output the method's body.
Here's how my ListBox_SelectedItemChanged event looks like:
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
textBox.Text = "";
textBox.Text = GetMethodBodyByName(method[listBox.SelectedIndex].Name);
}
That's All Folks!
Note: Be careful when doing this approach as if when you request names, different methods can have the same names. But that's a cake for another day, I'm done for now! take care bye-bye!
Working our way up for the Ultimate Solution!
The WPF MainWindow Forms carry with themselves two little useful properties, they are: Tag and Content, the idea is the following one:
With the Tag and Content Property we can assign any values to it that later it can be retrieved On-The-Fly without having to depend on Methods names specifically for this task.
So you would instead of looping each method and get its name respectively you can just do the way I did:
Iterate through the Method, and assign its body to the Tag property, and its name to the Content property, as this last property is the one that handles the actual Title property, so disregarding anything you do with the method in the future and even if it had the same name of another one, it will work no matter what.
How Can We Implement It?
Simply:
<...>
// Inside Method Body iteration routine...
<...>
var instr = mdInfo.Body.Instructions;
// Allocate in a new `ListBoxItem` each method and add it to the current listbox with their
// ... respective Tag and Content information... // Many Thanks Kao :D
newItem = new ListBoxItem();
newItem.Content = mdInfo.Name;
newItem.Tag = string.Join("\r\n", instr);
method.Add(mdInfo);
listBox.Items.Add(newItem);
Then on your SelectedItem Index-Value-Changed Event put this:
MSILTextBox.Clear();
// Retrieve them given the selected index...
// ... the returned value will be the Tag content of the ...
// ... previously saved item.
string getTag= ((ListBoxItem)listBox.SelectedItem).Tag.ToString();
MSILTextBox.Text = getTag;

Combobox crashes when selecting item with keyboard

I have a problem with the code below, basically what is does is read from a path where files with extension .config are stored, it reads the names of the files without extension and displays them all in a combobox. That works fine and if you click on the down arrow and select a name it actually does what it's supposed to, however, once I have selected an item from the dropdown with the mouse and I go back and start typing inside the combobox my application crashes throwing a exception.
I've tried adding a try-catch-finally but it keeps throwing the same error. Could it be the loop that is causing my application to crash once I start typing in the combobox?
p.d. If I just use the mouse to select an item from the dropdown menu my application works fine but once I've selected an item with the mouse and use the keyboard to type another item name inside the combobox my app crashes. Any pointers would be helpful.
// Gets all the file names from the path assigned to templatePath
// and assigns it to the string array fname
string[] fname = Directory.GetFiles(templatePath);
// Begin sorting through the file names assigned to the string array fname
foreach (string file in fname)
{
// Remove the extension from the file names and compare the list with
// the dropdown selected item
if (System.IO.Path.GetFileNameWithoutExtension(file) == cbTemplates.SelectedItem.ToString())
{
// StreamReader gets the contents from the found file and assigns
// them to the labels
using (var obj = new StreamReader(File.OpenRead(file)))
{
lbl1.Content = obj.ReadLine();
lbl2.Content = obj.ReadLine();
lbl3.Content = obj.ReadLine();
lbl4.Content = obj.ReadLine();
lbl5.Content = obj.ReadLine();
lbl6.Content = obj.ReadLine();
lbl7.Content = obj.ReadLine();
lbl8.Content = obj.ReadLine();
lbl9.Content = obj.ReadLine();
lbl10.Content = obj.ReadLine();
obj.Dispose();
}
}
}
My guess is this is probably causing the error:
cbTemplates.SelectedItem.ToString()
When you start typing in the combobox, the SelectedItem becomes null.
You should test whether the cbTemplates.SelectedItem is null before attempting to invoke ToString() on it. And if you're trying to match on the text of the combo-box, you might try using cbTemplates.Text instead.
And as others commented on your question, you don't need to call Dispose inside using and you should consider the possibility that the file might not contain 10 lines..

GeneratorPosition Error on Bound Observable Collection

I am using a Transitionals Slideshow control which has an observable collection of strings bound to the itemsource. These strings are the file paths to each picture in the slidehow. When I first load the WPF app, it runs this method correctly (using a directory path to generate the PicSlideShowCollection):
public void SelectImages(string path)
{
// Validate
if (string.IsNullOrEmpty(path)) throw new ArgumentException("path");
PicSlideShowCollection.Clear();
// Get directory info for specified path
DirectoryInfo di = new DirectoryInfo(path);
// Image mask
string[] extensions = new string[] { "*.jpg", "*.png", "*.gif", "*.bmp" };
// Search for all
foreach (string extension in extensions)
{
foreach (FileInfo fi in di.GetFiles(extension.ToLower()))
{
PicSlideShowCollection.Add(fi.FullName);
}
}
}
However, I have a button that allows the user to change the directory of images to use in the slideshow and re-runs the above method. When that is executed, I get this error:
GeneratorPosition '-1,1' passed to Remove does not have Offset equal
to 0.
This occurs on the PicSlideShowCollection.Clear() instruction.
If I comment that instruction, the new directory images get ADDED TO the original directory pictures which is NOT what I want.
I know this has to do with the PicSlideShowCollection being used as an item source to the Slide show control, but I need to know how I can prevent this error from occuring.
Thank you!
Slideshow.AutoAdvance = false;
Slideshow.SelcetedIndex=-1;
var count=PicSlideShowCollection.Count;
forearch(var item in newsources)
{
PicSlideShowCollection.Add(item);
}
while(count--)
PicSlideShowCollection.RemoveAt(0);
Slideshow.SelcetedIndex=0;
I can't explain why this error occurs. GeneratorPosition is used by the ItemContainerGenerator of an ItemsControl, which should simply work when you bind to its ItemsSource property and add or remove items to/from the source collection. Clearing the source collection is of course also a valid operation.
A possible workaround for the problem would be to reset the ItemsSource each time you switch to another image directory. So instead of clearing the existing collection
PicSlideShowCollection.Clear();
create a new collection and set ItemsSource to the new collection:
PicSlideShowCollection = new ObservableCollection<string>();
slideShowControl.ItemsSource = PicSlideShowCollection;

Tooltips for CheckedListBox items?

Is there a straighforward way to set additional text to appear in a tooltip when a user's mouse is held over an item in a CheckedListBox?
What I would expect to be able to do in code is:
uiChkLstTables.DisplayOnHoverMember = "DisplayOnHoverProperty"; //Property contains extended details
Can anyone point me in the right direction to do this? I've already found a couple of articles that involve detecting which item the mouse is currently over and creating a new tooltip instance, but this sounds a little too contrived to be the best way.
Thanks in advance.
Add a Tooltip object to your form and then add an event handler for the CheckedListBox.MouseHover that calls a method ShowToolTip();
Add MouseMove event of your CheckedListBox which has the following code:
//Make ttIndex a global integer variable to store index of item currently showing tooltip.
//Check if current location is different from item having tooltip, if so call method
if (ttIndex != checkedListBox1.IndexFromPoint(e.Location))
ShowToolTip();
Then create the ShowToolTip method:
private void ShowToolTip()
{
ttIndex = checkedListBox1.IndexFromPoint(checkedListBox1.PointToClient(MousePosition));
if (ttIndex > -1)
{
Point p = PointToClient(MousePosition);
toolTip1.ToolTipTitle = "Tooltip Title";
toolTip1.SetToolTip(checkedListBox1, checkedListBox1.Items[ttIndex].ToString());
}
}
Alternately, you could use a ListView with checkboxes instead. This control has
builtin support for tooltips.
Contrived or not; that's what there is...
I'm not aware of an easier way than you have already described (although I'd probably re-use a tooltip instance, rather than creating new all the time). If you have articles that show this, then use them - or use a 3rd party control that supports this natively (none leap to mind).
I would like to expand upon Fermin's answer in order to perhaps make his wonderful solution slightly more clear.
In the form that you're working in (likely in the .Designer.cs file), you need to add a MouseMove event handler to your CheckedListBox (Fermin originally suggested a MouseHover event handler, but this did not work for me).
this.checkedListBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.showCheckBoxToolTip);
Next, add two class attributes to your form, a ToolTip object and an integer to keep track of the last checkbox whose tool tip was shown
private ToolTip toolTip1;
private int toolTipIndex;
Finally, you need to implement the showCheckBoxToolTip() method. This method is very similar to Fermin's answer, except that I combined the event callback method with the ShowToolTip() method. Also, notice that one of the method parameters is a MouseEventArgs. This is because the MouseMove attribute requires a MouseEventHandler, which then supplies MouseEventArgs.
private void showCheckBoxToolTip(object sender, MouseEventArgs e)
{
if (toolTipIndex != this.checkedListBox.IndexFromPoint(e.Location))
{
toolTipIndex = checkedListBox.IndexFromPoint(checkedListBox.PointToClient(MousePosition));
if (toolTipIndex > -1)
{
toolTip1.SetToolTip(checkedListBox, checkedListBox.Items[toolTipIndex].ToString());
}
}
}
Run through your ListItems in your checkbox list of items and set the appropriate text as the item 'title' attribute, and it will display on hover...
foreach (ListItem item in checkBoxList.Items)
{
//Find your item here...maybe a switch statement or
//a bunch of if()'s
if(item.Value.ToString() == "item 1")
{
item.Attributes["title"] = "This tooltip will display when I hover over item 1 now, thats it!!!";
}
if(item.Value.ToString() == "item 2")
{
item.Attributes["title"] = "This tooltip will display when I hover over item 2 now, thats it!!!";
}
}

Categories