SelectedItems.Count exception in listview with VirtualMode - c#

after selecting a value out of my listview and clicking my button i wanted to get my value into the Code but my code is throwing this exception:
Count = 'this.listView1.SelectedItems.Count' threw an exception of type 'System.InvalidOperationException'
private void OK_button_Click(object sender, EventArgs e)
{
try
{
// OK -> Daten übernehmen
ListView.SelectedListViewItemCollection data = this.listView1.SelectedItems;
int iCount = data.Count;
if (iCount != 1)
{
MessageBox.Show("Value is empty");
return;
}
DialogResult = DialogResult.OK;
Close();
}
catch (Exception ex)
{
//WriteProtokoll(ex.ToString(), 0);
Close();
}
}
}
private void listView1_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
{
e.Index = Array.FindIndex(myData, s => s == textBox1.Text.ToString());
}
private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = new ListViewItem(myData[e.ItemIndex]);
}
myData = new string[dataListSize];
for (int i = 0; i < dataListSize; i++)
{
myData[i] = String.Format("{0}", i);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
String MyString = textBox1.Text.ToString();
ListViewItem lvi = listView1.FindItemWithText(MyString.TrimEnd());
//Select the item found and scroll it into view.
if (lvi != null)
{
listView1.SelectedIndices.Clear();
listView1.SelectedIndices.Add(lvi.Index);
listView1.EnsureVisible(lvi.Index);
}
}

This is by design when you use VirutalMode. The documentation states:
In virtual mode, the Items collection is disabled. Attempting to access it results in an InvalidOperationException. The same is true of the CheckedItems collection and the SelectedItems collection.
We can confirm this in the source code.
It goes on to provide the following advice:
If you want to retrieve the selected or checked items, use the SelectedIndices and CheckedIndices collections instead.
You should therefore use this.listView1.SelectedIndices.Count instead.
Looking at the source code again, we can see that this won't throw an exception.

Related

cant get my Value in Listview Virtualmode

after clicking my value and pressing my OK_button I cant get the Value out of the listView to save it somewhere else. I cant use listView1.FindItemWithText because I don't have a text to search for.. Idk how to look for the clicked value after I pressed the OK_button
//Create dummy data to display
myData = new string[dataListSize];
for (int i = 0; i < dataListSize; i++)
{
myData[i] = String.Format("{0}", i);
}
}
private void listView1_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
{
e.Index = Array.FindIndex(myData, s => s == textBox1.Text.ToString());
}
private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = new ListViewItem(myData[e.ItemIndex]);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
String MyString = textBox1.Text.ToString();
ListViewItem lvi = listView1.FindItemWithText(MyString.TrimEnd());
//Select the item found and scroll it into view.
if (lvi != null)
{
listView1.SelectedIndices.Clear();
listView1.SelectedIndices.Add(lvi.Index);
listView1.EnsureVisible(lvi.Index);
}
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e){ }
private void OK_button_Click(object sender, EventArgs e)
{
try
{
// OK -> Daten übernehmen
int iCount = this.listView1.SelectedIndices.Count;
if (iCount != 1)
{
MessageBox.Show("Value is empty");
return;
}
DialogResult = DialogResult.OK;
Close();
}
catch (Exception)
{
//WriteProtokoll(ex.ToString(), 0);
Close();
}
}
I found out that I can get my value with:
string txt = listView1.FocusedItem.Text;

How to re-enable listbox when removing a specified item from an output listbox

I am trying to re-enable only the listbox that pertains to the item that has been removed from the output listbox. For example, If I select "Wagon" from Body Type listbox and "Advance" from Package listbox, the listbox named "lstOutPut" displays the following:
What I want to do is, if I were to remove "SUV", I would only want for Body Type listbox re-enabled and not the rest
Here is my code
private void lstBody_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstBody.SelectedItem != null)
{
lstOutput.Items.Add(lstBody.SelectedItem);
lstBody.SelectionMode = SelectionMode.None;
lstBody.Enabled = false;
}
}
private void lstPackage_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstPackage.SelectedItem != null)
{
lstOutput.Items.Add(lstPackage.SelectedItem);
lstPackage.SelectionMode = SelectionMode.None;
lstPackage.Enabled = false;
}
}
And this is what I have for the remove button
private void btnRemove_Click(object sender, EventArgs e)
{
//remove selected item only
while (lstOutput.SelectedItems.Count > 0)
{
lstOutput.Items.Remove(lstOutput.SelectedItems[0]);
}
lstBody.Enabled = true;
lstPackage.Enabled = true;
}
Any ideas?
Thanks.
Assuming there would not be duplicates values among the different input listboxes,you could do the following in the btnRemove_Click event
var inputListBoxes = new[] { lstPackage, lstBody };
while (lstOutput.SelectedItems.Count > 0)
{
inputListBoxes.First(x => x.Items.Contains(lstOutput.SelectedItems[0])).Enabled = true;
lstOutput.Items.Remove(lstOutput.SelectedItems[0]);
}
For each listbox, enable it if non of its items exist in lstOutput:
var listBoxes = new ListBox[] {body, package, wheel, accessories};
foreach(var lst in listBoxes)
lst.Enabled = !lst.Items.Cast<string>().Any(x => lstOutput.Items.Contains(x));

ListBoxItem index on ListBox mouseover

I have a ListBox in a WPF application that has a MouseMove event handler attached. What I would like to do is to use this event to get the index of the item the mouse is over.
Simplified example of my code:
<StackPanel>
<ListBox x:Name="MyList" MouseMove="OnMouseMove"/>
<Separator/>
<Button>Beep</Button>
</StackPanel>
public CodeBehindConstructor()
{
List<string> list = new List<string>();
list.Add("Hello");
list.Add("World");
list.Add("World"); //Added because my data does have duplicates like this
MyList.ItemsSource = list;
}
public void OnMouseMove(object sender, MouseEventArgs e)
{
//Code to find the item the mouse is over
}
I would try using ViusalHelper HitTest method for that, something like this :
private void listBox_MouseMove(object sender, MouseEventArgs e)
{
var item = VisualTreeHelper.HitTest(listBox, Mouse.GetPosition(listBox)).VisualHit;
// find ListViewItem (or null)
while (item != null && !(item is ListBoxItem))
item = VisualTreeHelper.GetParent(item);
if (item != null)
{
int i = listBox.Items.IndexOf(((ListBoxItem)item).DataContext);
label.Content = string.Format("I'm on item {0}", i);
}
}
Try this:
public void OnMouseMove(object sender, MouseEventArgs e)
{
int currentindex;
var result = sender as ListBoxItem;
for (int i = 0; i < lb.Items.Count; i++)
{
if ((MyList.Items[i] as ListBoxItem).Content.ToString().Equals(result.Content.ToString()))
{
currentindex = i;
break;
}
}
}
You can also try this much shorter option:
public void OnMouseMove(object sender, MouseEventArgs e)
{
int currentindex = MyList.Items.IndexOf(sender) ;
}
However I'm not too sure whether it will work with your method of binding.
Option 3:
A little hacky but you could get the point value of the current location and then use IndexFromPoint
E.g:
public void OnMouseMove(object sender, MouseEventArgs e)
{
//Create a variable to hold the Point value of the current Location
Point pt = new Point(e.Location);
//Retrieve the index of the ListBox item at the current location.
int CurrentItemIndex = lstPosts.IndexFromPoint(pt);
}

How to disable button by selecting comboBox value

I have a comoboBox that is binded to sql database, and I added a default text at index 0 like this
string s = "< -------------Select an application ----------->";
applicationComboBox.Items.Insert(0, s);
applicationComboBox.SelectedIndex = 0;
I am wondering if there is a way to disable my button if the the string s at index 0 is select? In my comboBox, I binded the data with the while(SQLReader.Read()) method instead of using ValueMember and `DisplayMember
Here is what I tried but no luck
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
for (int i = 1; i < applicationComboBox.Items.Count; i++)
{
string value = applicationComboBox.GetItemText(applicationComboBox.Items[0]);
string s = "<------------- Select an application ----------->";
if (value == s)
{
exportButton.Enabled = false;
MessageBox.Show(value); //nothing happen
this.teacherCheckListBox.DataSource = null;
teacherCheckListBox.Items.Clear();
}
else
{
exportButton.Enabled = true;
}
}
}
}
Use SelectedIndex property to know which item is selected and disable the button if it is first item.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
{
exportButton.Enabled = false;
}
}

Property returns different value than what was set

I am working on a Visual Studio extension and my current goal is to set up a menu item in the Tools menu. When clicked on this menu item will open a WinForms window containing a ListView, 3 textboxes, and a button. The idea is when you click on one of the rows in the ListView the data from that row will be populated in the textboxes so that you can update it. If you click the button a new row is added and the textboxes are cleared. However, I'm having an issue with getting the index of the row that I've selected.
private int _index;
private void newSourceBtn_Click(object sender, EventArgs e)
{
// Add new row to the ListView
ListViewItem row = new ListViewItem();
row.SubItems.Add("new");
row.SubItems.Add(String.Empty);
row.SubItems.Add(String.Empty);
remoteSourceListView.Items.Add(row);
int index = remoteSourceListView.Items.Count - 1;
remoteSourceListView.Items[index].Selected = true;
newSourceAdded = true;
sourceNameTextBox.Clear();
sourceUrlTextBox.Clear();
}
public void SourceName_TextChanged(object sender, EventArgs e)
{
remoteSourceListView.Items[IndexSelected].SubItems[1].Text = sourceNameTextBox.Text;
}
public void SourceURL_TextChanged(object sender, EventArgs e)
{
string url = sourceUrlTextBox.Text;
if ((url.StartsWith("http")) || (url.StartsWith("https")) || (url.StartsWith("git")))
{
sourceBranchTextBox.Enabled = true;
}
remoteSourceListView.Items[IndexSelected].SubItems[2].Text = url;
}
public void SourceBranch_TextChanged(object sender, EventArgs e)
{
}
public void SourcesListView_SelectedIndexChanged(object sender, EventArgs e)
{
ListView.SelectedListViewItemCollection selectedRows = remoteSourceListView.SelectedItems;
foreach (ListViewItem row in selectedRows)
{
sourceNameTextBox.Text = row.SubItems[1].Text;
sourceUrlTextBox.Text = row.SubItems[2].Text;
IndexSelected = row.Index;
if (row.SubItems[3].Text != "")
{
sourceBranchTextBox.Enabled = true;
sourceBranchTextBox.Text = row.SubItems[3].Text;
}
}
}
public int IndexSelected
{
get { return _index; }
set { _index = value; }
}
This code shows the button click event which adds the new row to the ListView, the text changed events for each of the textboxes which updates the row in the ListView (sorta), and the selected index changed event for the ListView which is where I'm getting the index of the row that was just selected. While debugging, I noticed that when I click on a row I'm getting the correct index in the selected index changed event; however, when I call IndexSelected from either of the text changed events it is always giving me a different index.
Any suggestions?
From the code posted I can't find any reason that explain the behavior documented.
A possible reason could be the insertion/deletion of new/existing ListViewItem in a position before the saved RowIndex.
However another approach is possible. Instead of keeping the RowIndex you could try to set a global property to the ListViewItem selected and reuse this instance when you need to set its subitems.
In this way you avoid problems if the number of ListViewItems change and some item is inserted/removed before the saved RowIndex. However a safeguard against a null value should be provided.
private ListViewItem CurrentItemSelected {get;set;}
......
public void SourcesListView_SelectedIndexChanged(object sender, EventArgs e)
{
ListView.SelectedListViewItemCollection selectedRows = remoteSourceListView.SelectedItems;
foreach (ListViewItem row in selectedRows)
{
sourceNameTextBox.Text = row.SubItems[1].Text;
sourceUrlTextBox.Text = row.SubItems[2].Text;
CurrentItemSelected = row;
if (row.SubItems[3].Text != "")
{
sourceBranchTextBox.Enabled = true;
sourceBranchTextBox.Text = row.SubItems[3].Text;
}
}
}
public void SourceName_TextChanged(object sender, EventArgs e)
{
if(CurrentItemSelected != null)
CurrentItemSelected.SubItems[1].Text = sourceNameTextBox.Text;
}
However, I am a bit perplexed by your code. Do you have the property MultiSelect set to true? Because if it is set to false then your code doesn't need to loop.
public void SourcesListView_SelectedIndexChanged(object sender, EventArgs e)
{
if(remoteSourceListView.SelectedItems.Count > 0)
{
// With MultiSelect = false; there is only one selected item.
CurrentItemSelected = remoteSourceListView.SelectedItems[0];
sourceNameTextBox.Text = CurrentItemSelected.SubItems[1].Text;
sourceUrlTextBox.Text = CurrentItemSelected.SubItems[2].Text;
if (CurrentItemSelected.SubItems[3].Text != "")
{
sourceBranchTextBox.Enabled = true;
sourceBranchTextBox.Text = CurrentItemSelected.SubItems[3].Text;
}
}
}

Categories