ListBox topIndex doesn't work - c#

I try to put the selectedIndex of a listbox at the top of the displayed list with this code :
private void textBox1_TextChanged(object sender, EventArgs e)
{
sourceListBox.SelectionMode = SelectionMode.One;
if (textBox1.Text != string.Empty)
{
int index = sourceListBox.FindString(textBox1.Text);
if (index != -1 && sourceListBox.SelectedIndex != index)
{
sourceListBox.ClearSelected();
sourceListBox.SetSelected(index, true);
sourceListBox.TopIndex = sourceListBox.SelectedIndex;
}
}
else
{
sourceListBox.ClearSelected();
}
sourceListBox.SelectionMode = SelectionMode.MultiExtended;
}
But the selected index is stuck at the bottom of the listbox :
And this is the only part of code that change the behavior of the listbox. How can I fix that ?

It looks like the call to sourceListBox.SelectionMode = SelectionMode.MultiExtended; is resetting the TopIndex. Setting the TopIndex after that call will work:
private void textBox1_TextChanged(object sender, EventArgs e)
{
int topIndex = sourceListBox.TopIndex;
sourceListBox.SelectionMode = SelectionMode.One;
if (textBox1.Text != string.Empty)
{
int index = sourceListBox.FindString(textBox1.Text);
if (index != -1 && sourceListBox.SelectedIndex != index)
{
sourceListBox.ClearSelected();
sourceListBox.SetSelected(index, true);
topIndex = sourceListBox.SelectedIndex;
}
}
else
{
sourceListBox.ClearSelected();
}
sourceListBox.SelectionMode = SelectionMode.MultiExtended;
sourceListBox.TopIndex = topIndex;
}

Remove the check against the surrent SelectedIndex and your code that sets the TopIndex will execute always, also if your current SelectedIndex is equal to the result of the FindString
private void textBox1_TextChanged(object sender, EventArgs e)
{
.....
int index = sourceListBox.FindString(textBox1.Text);
if (index != -1)
{
sourceListBox.ClearSelected();
sourceListBox.SetSelected(index, true);
sourceListBox.TopIndex = sourceListBox.SelectedIndex;
}
....
}

Change
sourceListBox.TopIndex = sourceListBox.SelectedIndex;
to
sourceListBox.TopIndex = index;

to a winforms listbox
SendMessage(listBox.Handle,LB_SETTOPINDEX, 0, 0);
will make the topmost item visible, guaranteed, WITHOUT
changing its selection state, which can be a nice touch when just starting an app and deserializing to load a listbox.

Related

Search by DisplayMember in ListBox

I have a ListBox which I populate like this:
var dtCustomers = db.GetTableBySQL(query).AsEnumerable().Select(rows =>
new CustomersModel
{
Name = rows.Field<string>("Name"),
ProjectKey = rows.Field<int>("ProjectKey")
});
lstCustomers.DataSource = dtCustomers.ToList();
lstCustomers.DisplayMember = "Name";
lstCustomers.ValueMember = "ProjectKey";
lstCustomers.ClearSelected();
Now I want to create TextBox with search button to look inside this list and search by item selected as:
private void btnSearch_Click(object sender, EventArgs e)
{
lstCustomers.SelectedItems.Clear();
for (int i = lstCustomers.Items.Count - 1; i >= 0; i--)
{
if (lstCustomers.Items[i].ToString().ToLower().Contains(txtSearch.Text.ToLower()))
{
lstCustomers.SetSelected(i, true);
}
}
lblitems.Text = lstCustomers.SelectedItems.Count.ToString() + "items found";
}
Problem is it never finds anything. I think it is because it is comparing by ValueMember instead of DisplayMember. Can I search in the list by DisplayMember?
You can use pattern matching for this since the underlying items will be your CustomersModel:
private void btnSearch_Click(object sender, EventArgs e)
{
lstCustomers.SelectedItems.Clear();
int matchCount = 0;
for (int i = lstCustomers.Items.Count - 1; i >= 0; i--)
{
if (lstCustomers.Items[i] is CustomersModel customer &&
customer.Name.IndexOf(txtSearch.Text, StringComparison.OrdinalIgnoreCase) > -1)
{
matchCount++;
lstCustomers.SetSelected(i, true);
}
}
lblItems.Text = $"{matchCount} item{(matchCount > 1 ? "s" : "")} found";
}

Remove TabPage from TabControl

So i need to dynamically add and delete some TabPage. User clicked "Show Tab" = storage_pageadded.
private void storage_menuItem_Click(object sender, EventArgs e) {
storage_page.Text = storage_page.Name = "Storage";
main_tabControl.TabPages.Add(storage_page);
main_tabControl.SelectedTab = storage_page;
}
And the when he chooses another page the storage_page has to be removed
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e) {
for (int i = 0; i < main_tabControl.TabPages.Count; i++) {
if (main_tabControl.TabPages[i].Name.Equals("storage", StringComparison.OrdinalIgnoreCase) && main_tabControl.SelectedTab.Name != "Storage") {
main_tabControl.TabPages.RemoveAt(i);
break;
}
}
}
When i click "Show Tab" page shows up. But when i select other page i see the ArgumentOutOfRangeException leading to Application.Run(new Form_Authentication()); line
How can i do that?
I think the problem is you increase i variable by 1 from 0 to main_tabControl.TabPages.Count
I assume that main_tabControl.TabPages.Count = 10, what is happended if you're removing 7th element? At that time, main_tabControl.TabPages.Count = 4 and i variable = 6. So, i variable is exceed the range of TabPages.
You should to change your code:
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e) {
for (int i = main_tabControl.TabPages.Count - 1; i >=0 ; i--) {
if (main_tabControl.TabPages[i].Name.Equals("storage", StringComparison.OrdinalIgnoreCase) && main_tabControl.SelectedTab.Name != "Storage") {
main_tabControl.TabPages[i].Dispose();
break;
}
}
}

SelectedIndices Changed Listbox

Is there some event I can use to tell when the SelectedIndices property changes for a listbox? I want to deselect items in a listbox based on a certain property value of the item. I've hooked up an event that works for when a SelectedIndex is changed, but not sure how to do it for when the SelectedIndices property changes for multiselection.
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = (Curve)listBox1.SelectedItem;
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
You could use ListBox.SelectedItems and LINQ to find all Curves with newName==null to deselect them:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var nullNameCurves = listBox1.SelectedItems
.Cast<Curve>()
.Where(c => c.newName == null)
.ToList();
listBox1.SelectedIndexChanged -= listBox1_SelectedIndexChanged;
foreach (Curve curve in nullNameCurves)
listBox1.SetSelected(listBox1.Items.IndexOf(curve), false);
listBox1.SelectedIndexChanged += listBox1_SelectedIndexChanged;
}
According to MSDN, this event will be fired every time the selection changes:
If the SelectionMode property is set to SelectionMode.MultiSimple or SelectionMode.MultiExtended, any change to the SelectedIndices collection, including removing an item from the selection, will raise this event.
So basically, you can use it the same way as using it with single selection.
Sample:
For example if you want to deselect all items with null as newName:
foreach (var item in listBox1.SelectedItems)
{
if ((item as Curve).newName == null)
{
int index = listBox1.SelectedItems.IndexOf(item);
listBox1.SetSelected(index, false);
}
}
(I'm not sure if you can deselect items inside a foreach loop since it changes the SelectedItems object itself. If it does not work, you can still make a temporary list of those items and deselect them after the loop.)
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = null;
for (int i = 0; i < listBox1.SelectedItems.Count; i++)
{
curItem = (Curve)listBox1.SelectedItems[i];
if (curItem != null)
{
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
}
}

how to check only one item in checkedlistbox

I have a check list box control and I want to select only one item at a time and I am currently using this code to do the same.
private void CLSTVariable_ItemCheck(object sender, ItemCheckEventArgs e)
{
// Local variable
int ListIndex;
CLSTVariable.ItemCheck -= CLSTVariable_ItemCheck;
for (ListIndex = 0;
ListIndex < CLSTVariable.Items.Count;
ListIndex++)
{
// Unchecked all items that is not currently selected
if (CLSTVariable.SelectedIndex != ListIndex)
{
// set item as unchecked
CLSTVariable.SetItemChecked(ListIndex, false);
} // if
else
{
// set selected item as checked
CLSTVariable.SetItemChecked(ListIndex, true);
}
} // for
CLSTVariable.ItemCheck += CLSTVariable_ItemCheck;
}
this code is working fine.
but problem is that when I click again and again on selected item then that selected item should not be unchecked, means at least one item should be checked always...
I agree with commentators above - you should consider using radiobuttons. But if you really need CheckedListBox, then use this ItemChecked event handler instead:
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (checkedListBox1.CheckedItems.Count == 1)
{
Boolean isCheckedItemBeingUnchecked = (e.CurrentValue == CheckState.Checked);
if (isCheckedItemBeingUnchecked)
{
e.NewValue = CheckState.Checked;
}
else
{
Int32 checkedItemIndex = checkedListBox1.CheckedIndices[0];
checkedListBox1.ItemCheck -= checkedListBox1_ItemCheck;
checkedListBox1.SetItemChecked(checkedItemIndex, false);
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}
return;
}
}
Well, it was an answer to me! I couldn't get the above code to work in the checkedListBox1_ItemCheck. I had to modify a portion of it ans include it in the checkedListBox1_SelectedIndexChanged event. But I couldn't remove the original code all together. Here is what I've added...
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (checkedListBox1.CheckedItems.Count > 1)
{
Int32 checkedItemIndex = checkedListBox1.CheckedIndices[0];
checkedListBox1.ItemCheck -= checkedListBox1_ItemCheck;
checkedListBox1.SetItemChecked(checkedItemIndex, false);
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}
}
Which is basically, if you have more than 1 box checked, switch the last one for the new one. I'm curious why the original code didn't work. And why it has to be there for my new code to work? Thank you.
I found this code it work so well
private void chkboxmov_ItemCheck(object sender, ItemCheckEventArgs e)
{
for (int ix = 0; ix < chkboxmov.Items.Count; ++ix)
if (ix != e.Index)
chkboxmov.SetItemChecked(ix, false);
}
"at least one item should be checked always"
The current solution (the last one) allows items to be checked off. If your purpose is to select exactly one item at all times, use this as a MouseUp event,
private void ChklbBatchType_MouseUp(object sender, MouseEventArgs e)
{
int index = ((CheckedListBox)sender).SelectedIndex;
for (int ix = 0; ix < ((CheckedListBox)sender).Items.Count; ++ix)
if (index != ix) { ((CheckedListBox)sender).SetItemChecked(ix, false); }
else ((CheckedListBox)sender).SetItemChecked(ix, true);
}

Check if flowLayoutPanel is empty in c#

I want to make an error label come up when my flowLayoutPanel is empty, but i don't know how to check that the flowLayoutPanel is empty. This is my current code:
private void flowLayoutPanel1_ControlRemoved(object sender, ControlEventArgs e)
{
if (flowLayoutPanel1.Controls == null)
{
customtoolwarning.Visible = true;
}
else
{
customtoolwarning.Visible = false;
}
}
Please Help,
Thanks
private void flowLayoutPanel1_ControlRemoved(object sender, ControlEventArgs e)
{
if (flowLayoutPanel1.Controls.Count > 0)
{
customtoolwarning.Visible = true;
}
else
{
customtoolwarning.Visible = false;
}
}
The problem you're running into is you're checking Controls for null to determine if it's empty. The Controls property won't ever be null but instead will be non-null and have 0 length when empty. For example
if (flowLayoutPanel1.Controls.Count == 0) {
// It's empty
}
lblNoContacts.Visible = (flowLayoutPanel.Controls.Count == 0) ? true : false;

Categories

Resources