ListView selection

G

Guest

I have a ListView control, and anytime I manually set the selected item, the
original item OnSelectedIndexChanged event fires twice.

Here is a sample of what I am doing in the OnSelectedIndexChanged function:

if ( listView1.SelectedItems.Count == 1 )
{
MessageBox.Show( "index = " +
listView1.SelectedIndices[0].ToString() );
if ( isinternal )
return;
isinternal = true;
if ( last >= 0 ) // last is the last selected index
{
MessageBox.Show( "Not Shown" );
//behaves regardless if the next line is present
listView1.SelectedItems.Clear();
listView1.Items[0].Selected = true;
}
last = listView1.SelectedIndices[0];
isinternal = false;
}
else if ( listView1.SelectedItems.Count == 0 )
{

}

I have 4 items in my ListView. Assume I select index 4. I should see message
boxes in this order:

index = 4
Not Shown
index = 0

However, I really see
index = 4
Not Shown
index = 0
index = 4
Not Shown
index = 0

If I don't manually set the selected index to 0, the control behaves as
expected. Why does manually setting the selected index cause this behavior?
How can I manually set this index and avoid this behavior?

Thanks in advance for your help!
 
S

Stoitcho Goutsev \(100\)

JL,

Can you post complete compilable sample that demonstrates the problem?
 
G

Guest

Here is all the code from my Form1.cs file:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Text;

namespace APISampleCode
{
public class Form1 : System.Windows.Forms.Form
{
private ListView listView1;
private bool isinternal = false;
private int last = -1;

private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
}

protected override void Dispose(bool disposing)
{
if ( disposing )
{
if ( components != null )
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
private void InitializeComponent()
{
System.Windows.Forms.ListViewItem listViewItem1 = new
System.Windows.Forms.ListViewItem( "Item 1" );
System.Windows.Forms.ListViewItem listViewItem2 = new
System.Windows.Forms.ListViewItem( "Item 2" );
System.Windows.Forms.ListViewItem listViewItem3 = new
System.Windows.Forms.ListViewItem( "Item 3" );
System.Windows.Forms.ListViewItem listViewItem4 = new
System.Windows.Forms.ListViewItem( "Item 4" );
this.listView1 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.Items.AddRange( new
System.Windows.Forms.ListViewItem[] {
listViewItem1,
listViewItem2,
listViewItem3,
listViewItem4} );
this.listView1.Location = new System.Drawing.Point( 32, 25 );
this.listView1.MultiSelect = false;
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size( 190, 137 );
this.listView1.TabIndex = 9;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.SelectedIndexChanged += new System.EventHandler(
this.listView1_SelectedIndexChanged );
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size( 5, 13 );
this.ClientSize = new System.Drawing.Size( 250, 207 );
this.Controls.Add( this.listView1 );
this.Name = "Form1";
this.StartPosition =
System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Create a new ProForm file";
this.ResumeLayout( false );

}
#endregion

[STAThread]
static void Main()
{
Application.Run( new Form1() );
}

private void listView1_SelectedIndexChanged(object sender, EventArgs
e)
{
if ( listView1.SelectedItems.Count == 1 )
{
MessageBox.Show( "index = " +
listView1.SelectedIndices[0].ToString() );
if ( isinternal )
return;
isinternal = true;
if ( last >= 0 )
{
MessageBox.Show( "Not Shown" );
listView1.SelectedItems.Clear();
listView1.Items[0].Selected = true;
}
last = listView1.SelectedIndices[0];
isinternal = false;
}
else if ( listView1.SelectedItems.Count == 0 )
{

}
}
}
}
 
C

Claes Bergefall

I'm not getting that behaviour, but I'm having a hard time understanding
your code. What exactly are you trying to accomplish?

/claes
 
G

Guest

Is there something that I am doing wrong to cause that behavior?

This is just a sample program that displays the same behavior as my real
application. In the real application, the ListView selection populates data
in text boxes. When the user selects an item, I will check the previous
selection for errors. If I find an error, I will display a message box
stating what the error is and "cancel" the new index selection. The code that
I have accomplishes this by manually setting the selected index to the
previously selected index, with the exception that I am geting the error
message box twice.

The sample code shows this by always assuming that there is an error and
returning the selected index to index 0.

Is there a better way of doing this?

Thanks!
 
N

Norman Yuan

It is by design taht sometimes the SelectedIndex_Changed event fires twice,
if, for example, when there is an item is selected and then user click
another item. In this case, following things occur: the previously selected
item gets deselected (SelectedIndex changed from a iten index to -1) and
then the clicked item gets selected (SelectedIndex changed from -1 to a new
index).

So, you have to handle it correctly in ListView_SelectedIndexChanged() event
handler, if you only want to do something on the currently selected item.
For example, like this:

private void ListView1_SelectedINdexChanged(object sender, ...)
{
if (ListView1.SelectedItems.Count==0) return;
//or if (ListView1.SelectedIndex<0) return;

//Then do something with the selected item here
MessageBox.Show(ListView1.SelectedItems[0].Text);
}
 
G

Guest

I am already checking to make sure that an index is selected before
displaying my messagebox or manually setting a different index.

If the user has index 0 currently selected, then clicks on index 3, in the
SelectedIndex_Changed event handler, after checking to make sure that an
index has been selected, I want to manually set the index back to 0.

If I ran your example below, I would see the following message boxes:

Index 3
Index 0
Index 3
Index 0

The user only clicked on index 3 one time, so I should have only seen the
Index 3 message once not twice. Do you know anyway around this? It seems like
whenever I programatically set the selected index, it will cause the user
choosen index to fire twice.

private void ListView1_SelectedINdexChanged(object sender, ...)
{
if (ListView1.SelectedItems.Count==0) return;
//or if (ListView1.SelectedIndex<0) return;

//Then do something with the selected item here
MessageBox.Show(ListView1.SelectedItems[0].Text);
}
 
N

Norman Yuan

Since you change the selected item in the SelectedIndex_Changed event by
code (you always re-set the selected item back to Item[0], right?), so, as
long as the user click any item other than Item[0], the MessageBox in
SelectedIndex_Changed event handler will show mutiple times, in following
logic order, suppose cuurent selected item is item[0]:

1. user click item[3],
a. SelectedIndex changed from 3 to -1. SelectedIndex_Changed event
fires. Since in your code you used "if ListView1.SelectedItems.Count==1..."
so no MessageBox is shown;
b. SelectedIndex changed from -1 to 3. SelectedIndex_Changed event
fires. Now that there is item selected, according to your code, the
messageBox shows. AND then your code to chane selected item back item[0]
executes!
2. Due to your code to set selected item back to item[0],
a. SelecctedIndex changed to -1. SelectedIndex_Changed event fires. By
your code, nothing happens;
b. SelectedIndex changed from -1 to 0. SelectedIndex_Changed event
fires. According to your code, MessageBox shows again and your code to reset
selected item executes AGAIN. but this time, since item[0] is already
selected, your code does not cause SelectedIndex_Changed event firing,
because SelectedIndex does not change.

So, according your code, if you click any item other than item[0], your code
will show MessageBox twice. The effect of you code is to not allow user to
have other item to be selected, except for item[0], is that what you want?
 
G

Guest

Thanks for your response. Here is a better explanation of what I am trying to
do.

This is just a sample program that displays the same behavior as my real
application. In the real application, the ListView selection populates data
in text boxes. When the user selects an item, I will check the previous
selection for errors. If I find an error, I will display a message box
stating what the error is and "cancel" the new index selection. The code that
I have accomplishes this by manually setting the selected index to the
previously selected index, with the exception that I am geting the error
message box twice.

The sample code shows this by always assuming that there is an error and
returning the selected index to index 0. In the real application, I will
programatically set the selected index to whatever the last selected index
was.

Is there a better way of doing this?

Aside from making sure I am only checking for errors when
selecteditems.count == 1, I also have a flag that is set before the index is
changed programatically. When you enter the OnSelectedIndexChanged event
handler and the flag is true and the selected index count == 1, the function
returns immediately and does not perform another error check. Please see my
full code listing that I posted in response to Stoitcho Goutsev.

Here is a snapshot of what I am seeing. Assume the current selection is
Index 0.

1. The user clicks on index 2.
a. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == -1.
b. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == 1 (selected index = 2).
c. Errors are found, message box displayed, set InternalFlag == true, and
set Items[0].Selected == true
d. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == -1
e. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == 1 (selected index = 0) -- however, internal flag is
true so return before doing any further actions
f. Return to call setting the selected index, set InternalFlag == false,
and continue
2. The user does not click on index 2 again, however all steps are repeated.
a. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == -1.
b. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == 1 (selected index = 2).
c. Errors are found, message box displayed, set InternalFlag == true, and
set Items[0].Selected == true
d. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == -1
e. OnSelectedIndexChanged event handler is entered with
SelectedItems.Count == 1 (selected index = 0) -- however, internal flag is
true so return before doing any further actions
f. Return to call setting the selected index, set InternalFlag == false,
and continue

As the user only clicks on index 2 one time, I should only be seeing all
events under 1. For some reason I am also seeing all events under 2. This is
what I am trying to get assistance to correct. Do you have any ideas why this
could be firing twice?

Thanks for your help!

Norman Yuan said:
Since you change the selected item in the SelectedIndex_Changed event by
code (you always re-set the selected item back to Item[0], right?), so, as
long as the user click any item other than Item[0], the MessageBox in
SelectedIndex_Changed event handler will show mutiple times, in following
logic order, suppose cuurent selected item is item[0]:

1. user click item[3],
a. SelectedIndex changed from 3 to -1. SelectedIndex_Changed event
fires. Since in your code you used "if ListView1.SelectedItems.Count==1..."
so no MessageBox is shown;
b. SelectedIndex changed from -1 to 3. SelectedIndex_Changed event
fires. Now that there is item selected, according to your code, the
messageBox shows. AND then your code to chane selected item back item[0]
executes!
2. Due to your code to set selected item back to item[0],
a. SelecctedIndex changed to -1. SelectedIndex_Changed event fires. By
your code, nothing happens;
b. SelectedIndex changed from -1 to 0. SelectedIndex_Changed event
fires. According to your code, MessageBox shows again and your code to reset
selected item executes AGAIN. but this time, since item[0] is already
selected, your code does not cause SelectedIndex_Changed event firing,
because SelectedIndex does not change.

So, according your code, if you click any item other than item[0], your code
will show MessageBox twice. The effect of you code is to not allow user to
have other item to be selected, except for item[0], is that what you want?

JL said:
I am already checking to make sure that an index is selected before
displaying my messagebox or manually setting a different index.

If the user has index 0 currently selected, then clicks on index 3, in the
SelectedIndex_Changed event handler, after checking to make sure that an
index has been selected, I want to manually set the index back to 0.

If I ran your example below, I would see the following message boxes:

Index 3
Index 0
Index 3
Index 0

The user only clicked on index 3 one time, so I should have only seen the
Index 3 message once not twice. Do you know anyway around this? It seems
like
whenever I programatically set the selected index, it will cause the user
choosen index to fire twice.

private void ListView1_SelectedINdexChanged(object sender, ...)
{
if (ListView1.SelectedItems.Count==0) return;
//or if (ListView1.SelectedIndex<0) return;

//Then do something with the selected item here
MessageBox.Show(ListView1.SelectedItems[0].Text);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top