I have a small project where I get some values from a txt and put inside of a ListView. I need to get the selected value when I click in some item, the first item that I ever select, works fine, but if I try to select again, I get an exception.
This is what I did..
Json = new StreamReader(openDialog.FileName).ReadToEnd();
var ParsedValue = JsonValue.Parse(Json);
Parsed = JsonConvert.DeserializeObject<List<Model>>(ParsedValue.ToString());
foreach (var item in Parsed)
{
var rows = new string[] { item.car, Convert.ToString(item.age )};
var items = new ListViewItem(rows)
{
Tag = item
};
ListViewCars.Items.Add(items);
}
The List view is Filled.
And to get the Item selected from the list :
private void cartsList_SelectedIndexChanged(object sender, EventArgs e)
{
ItemSelected = (Model)ListViewCars.SelectedItems[0].Tag;
}
I can only get the value that I select first when the program runs.
The exception :
System.ArgumentOutOfRangeException: 'InvalidArgument=Value of '0' is not valid for 'index'.
Parameter name: index'
Check out the documentation on ListView.SelectedIndexChanged. Specifically, look at the Remarks section, which reads:
The SelectedIndices collection changes whenever the Selected property of a ListViewItem changes. The property change can occur programmatically or when the user selects an item or clears the selection of an item. When the user selects an item without pressing CTRL to perform a multiple selection, the control first clears the previous selection. In this case, this event occurs one time for each item that was previously selected and one time for the newly selected item.
I added the emphasis. This means that when you select the second item, the currently selected item is unselected and SelectedIndexChanged is triggered before the new item is selected. So when you try to get the first selected item with ListViewCars.SelectedItems[0].Tag you get that ArgumentOutOfRangeException because there are no selected items.
You need to add a check to the top of your event handler to make sure that there is at least one selected item before accessing SelectedItems[0].
Related
I have a form that takes in an object in it's constructor and populates controls on the form from properties in that object. I am having an issue where I can't set a ComboBox's SelectedText property, or at least it isn't working how I expect it to.
public Form(ValueHoldingObject obj)
{
// yeah I know this is not a very clean way to populate the combobox, the issue
// isn't limited to the combobox so I don't think this is relevant
List<int> items = Repo.GetAllItems().Reverse();
foreach (int id in checkInPrizeIds.Take(100))
// Insert at beginning to put more recently used items at the top
combobox.Items.Insert(0, id);
combobox.DropDownHeight = 200;
combobox.SelectedText = obj.StringProperty;
}
When I am testing this form the text of the combobox isn't being populated. If I add a breakpoint on the line where I assign the text it DOES get assigned, so some event is firing (multiple focus change events probably) and making it work the way I want. Obviously I can't use a breakpoint as a fix in production code. Am I assigning this value incorrectly? Should I be using a different method to populate the values?
Further testing has reviled that it isn't just the combobox, all of my controls are only being populated correctly if I have the breakpoint.
In the constructor, you need to set the selected item, for example:
foreach ( var item in combobox.Items )
if ( (string)item == obj.StringProperty )
combobox.SelectedItem = item;
Or:
foreach ( var item in combobox.Items )
if ( (int)item == Convert.ToInt32(obj.StringProperty) )
combobox.SelectedItem = item;
It's confusing but despite its name, the property SelectedText is not really the selected item... because combo box items are objects and not strings: texts shown are a representation of the item objects using ToString().
Therefore setting the selected text will not guarantee to select an item and we can prefer setting the SelectedItem.
In addition to these considerations, you set the selected text property in the constructor after populating the combo box and that can cause problems because it is before the form and the control are drawn or something like that... that is to say perhaps before the ToString() methods are called on items to prepare the visual cache, so setting the selected text can't get a match with the list.
Setting the selected text selects an existing item if done in the form load or shown events.
private void Form_Load(object sender, EventArgs e)
{
combobox.SelectedText = obj.StringProperty;
}
ComboBox.SelectedText doesn't give me the SelectedText
ComboBox.SelectedText Property
For example if I click on the first item it will be at index 0.
If I click on item 15 then the index should be 16.
I tried
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
int index = listView1
}
But I'm not sure if this is the right event or I should use the listView1_Click event ?
And the listView1 does not have any property SelectedIndex.
And last thing is I want to get the item text according to the index of the item I clicked on.
Assuming you want the index of the currently selected item you can do it like this :
int index = ListView1.FocusedItem.Index
Use ListView.SelectedIndices property:
List<int> selectedIndices = listView1.SelectedIndices.Cast<int>().ToList();
It returns collection of selected indices (because by default you can select several items in listview if you click on items with Ctrl or Shift key pressed). Also note that when you deselect all items, this collection will be empty and things like listView1.SelectedIndices[0] will throw IndexOutOfRange exception.
But if you will set MultiSelect property to false. Then this collection will always contain zero or one item. You can use Count property of SelectedIndicesCollection to check if item was selected:
if (listView1.SelectedIndices.Count > 0)
{
int selectedIndex = listView1.SelectedIndices[0];
}
You need to use the selected indices list you can also do this be item too.
listView1.SelectedIndices[0]
First you can get listview item object like below
ListViewItem lst=(ListViewItem)listView.SelectedItems[0];
from that object(lst) you can get the text like below
string text=lst.Content.ToString();
According to MSDN, there is still SelectedIndex. In my opinion your event is wrong, but you can still see it by .SelectedIndex. As it was mentioned before.
UPDATE: according to the comment, the link is fixed for the correct case.
I have an object that has some attributes from the list selected - let's say a Promotion that can have 0 to X communication channels. To display/edit this information I am using a listbox with option SelectionMode==MultiExtended.
But in some cases it is behaving strangely
I have Promotion with 2 communication channels selected (first and last out of three channels),
I click on a second channel (that previously was the only unselected channel) and know it shows, that 1st and 2nd channels are selected (I placed a check at the beginning of the listbox SelectedIndexChanged event - and it shows that SelectedItems.Count==2, although I clicked on a single item not holding Ctrl or Shift keys) and in this case SelectedIndexChanged event is triggered twice in all other cases it is triggered just once
This happens only after the first time I open this dialogform, if I manually select 1st and 3rd item of Channels, and then click on the 2nd item - then it works properly
Screencast of a problem in action
http://screencast.com/t/lVs0e9oau
This is how I load list of all possible channels into listbox
foreach (var ct in Promotion_operations.Configuration.PromoCommunicationTypes)
{
KeyValuePair<string, PromotionCommunicationType> nct =
new KeyValuePair<string, PromotionCommunicationType>(ct.Name, ct);
communications.Add(nct);
}
PromotionCommunicationList.DataSource = communications; //Promotion_operations.Configuration.PromoCommunicationTypes;
PromotionCommunicationList.DisplayMember = "Key";
PromotionCommunicationList.ValueMember = "Value";
This is how I load selecteditems based on Promotion's data
private void LoadSelectedCommunicationsList(ListBox lstbox, List<PromotionCommunication> communications)
{
lstbox.SelectedItems.Clear();
foreach (var ct in communications)
{
for (int j = 0; j < lstbox.Items.Count; j++)
{
if (ct.CommunicationType.Id == ((KeyValuePair<string, PromotionCommunicationType>)lstbox.Items[j]).Value.Id)
{
lstbox.SelectedItems.Add(lstbox.Items[j]);
}
}
}
}
What could be the cause of this behaviour?
that clicking on one previously unselected list selects both - newly selected item and first item of the list?
Your PromotionCommunicationList and HistoryCommunicationList are sharing the same reference to your list of objects as DataSource. That said, they have the same BindingContext and share the same CurrencyManager. CurrencyManager is remembering selected items of your ListBox control and that's where your conflict is created because he's saving selected items of both of your ListBoxes. You already found the solution for your problem because new CurrencyManager is created when you set "different" list (the copy of your original one) as DataSource. Another possible solution would be the creation of new BindingContext for one of your ListBox controls.
You can try this out:
PromotionCommunicationList.DataSource = communications;
(..)
HistoryCommunicationList.BindingContext = new BindingContext(); // Add this
HistoryCommunicationList.DataSource = communications;
It should solve your problem. For more information about BindingContext check this link on MSDN.
I found the cause of the problem, though I don't really understand why it caused such a behaviour (if someone will answer that question, I will accept it as an answer to this question)
I had 2 listbox-es in my form and both of them where using the same collection as a Datasource, BUT!!! SelectedItems was selected using code (acctually it seems that in winforms it is not possible to databind listbox's selecteditems)
INITIALLY My code was:
PromotionCommunicationList.DataSource = communications;
(..)
HistoryCommunicationList.DataSource = communications;
Corrected version is:
PromotionCommunicationList.DataSource = communications.ToList();
(..)
HistoryCommunicationList.DataSource = communications.ToList();
I know that ToList() makes a copy, but I don't understand what's wrong with having the same collection as DataSource for list items of 2 listbox-es? Why does this have an impact on SelectedItems collection?
I have a drop down list control populated with items and some code to take the currently selected item value. The problem is I only get the value of the first item in the list regardless of what item is actually selected.
Here is my code to populate the drop down:
protected void displayCreateCategories()
{
StoreDataContext db = new StoreDataContext();
var a = from c in db.Categories
orderby c.Name
select new{catName= c.Name,
catId=c.CategoryID};
ddlCategory.DataSource = a;
ddlCategory.DataTextField = "catName";
ddlCategory.DataValueField = "catId";
ddlCategory.DataBind();
}
To get the value of the currently selected item which in my case is always of type integer I do label1.text=Convert.toInt32(ddlCategory.SelectedValue);
I get the selected value, but it is always for the 1st item in the list. I'm pulling my hair out over this. :(
I suspect you're running the list loading code every time the page loads, which is destroying the list, repopulating the list, and auto-selecting the first item before your selection retrieval code gets run.
Use this construction in Page_Load:
if (!IsPostBack)
{
// Initial control population goes here
}
Data binding will reset the control's selected value so make sure you retrieve the selected value before data binding on postback.
I have a CheckBoxList with a SelectedIndexChanged event, where I add the Value of the selected ListItem to a variable. I want to substract the Value when the item is unchecked.
I tried SelectedIndex but returns -1 and SelectedItem returns null. And the EventArgs argument doesn't have any data to help...
You should use the Items property. In other words, your variable should start with its value set to whatever it should be before applying the selected items. Then, iterate over the items, applying your logic against the variable one item at a time for each item that is selected. Then, no matter what the user selects or de-selects, you'll always arrive at the proper value for your variable.
int myValue = 0;
foreach(ListItem item in cbl.Items)
{
if(item.Selected) myValue += int.Parse(item.Value);
}