I have a multiselection listbox. I want to see the last selected item (which the user just selected). I can't find any way to do this googling..
Thanks in advance.
I have a multiselection listbox. I want to see the last selected item (which the user just selected). I can't find any way to do this googling..
Thanks in advance.
A compact expert note to complement the thread: the core choices are (A) track selection-state differences (the approach implemented) or (B) capture the user interaction that caused the change (simpler and usually more reliable for mouse-driven input). 's point about separating the logic out of the form is good practice — keep selection-tracking code in a small helper/monitor so the UI code stays clean.
A lightweight WinForms technique that avoids full list-diffing is to record the item under the mouse when the click happens, then use the normal SelectedIndexChanged/SelectedValueChanged to react. This also keeps mouse- and keyboard-driven flows separate:
string lastClickedItem = null;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
int idx = listBox1.IndexFromPoint(e.Location);
if (idx >= 0) lastClickedItem = listBox1.Items[idx].ToString();
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// lastClickedItem contains the item last clicked with the mouse;
// fallback to a diff or keyboard handler when null.
Debug.WriteLine("Last changed: " + (lastClickedItem ?? "<keyboard/fallback>"));
lastClickedItem = null;
} For web (HTML <select multiple>) a hybrid is safest: try to capture the clicked option with pointerdown (when available) and fall back to diffing previous/current selected sets on change. Example pattern:
const sel = document.querySelector('select[multiple]');
let lastClicked = null;
let previous = new Set([...sel.options].filter(o => o.selected).map(o => o.value || o.text));
sel.addEventListener('pointerdown', e => {
if (e.target.tagName === 'OPTION') lastClicked = e.target.value || e.target.text;
});
sel.addEventListener('change', () => {
const current = new Set([...sel.options].filter(o => o.selected).map(o => o.value || o.text));
const added = [...current].filter(x => !previous.has(x));
const removed = [...previous].filter(x => !current.has(x));
const last = lastClicked || (added.length ? added[added.length-1] : (removed[0] || null));
console.log('last changed:', last);
previous = current;
lastClicked = null;
}); Caveats: shift-range selects may produce multiple additions so "last" can be ambiguous unless a pointer event is available; keyboard-only users need a fallback (diff or explicit keyboard handlers); native multi-select is poor on mobile — checkboxes or a custom list UI (as discovered) are often the better UX.
Jump to Post— cgeier 187Here's a solution. Version 2 code has been tested.
Create a new class called "ItemInfo". Replace with the following code:
ItemInfo:
public class ItemInfo { public string Name { get; set; } public string SelectionType { get; set; } }In your form, (ex: Form1.cs), …
Jump to Post— Momerath 1,327There is no need to pass any of that stuff by reference, remove it all. You mix modes (windows controls writing to the console). Your methods have overly complicated signatures and almost all that code (if not all of it) should be inside a different class, not in your form. …
Here's a solution. Version 2 code has been tested.
Create a new class called "ItemInfo". Replace with the following code:
ItemInfo:
public class ItemInfo
{
public string Name { get; set; }
public string SelectionType { get; set; }
}
In your form, (ex: Form1.cs), do the following:
Create a list to hold the previously selected items and an instance of ItemInfo.
public partial class Form1 : Form
{
List<string> previousSelectedItemsList = new List<string>();
ItemInfo lastItem = new ItemInfo();
public Form1()
{
InitializeComponent();
}
}
Then we create a method to find the last selected item:
Version 1
(keeps track of last selected item):
private void getLastItemChanged(ref ListBox lb)
{
List<string> lastItemSelected;
List<string> currentSelectedItemsList = new List<string>();
//put selected items into a list
//so we can use list.except
for (int i = 0; i < lb.SelectedItems.Count; i++)
{
//add selected items to list
currentSelectedItemsList.Add(lb.SelectedItems[i].ToString());
//Console.WriteLine("item: " + lb.SelectedItems[i]);
}//for
//get last item selected as a list
//by comparing the current list
//to the previous list
lastItemSelected = currentSelectedItemsList.Except(previousSelectedItemsList).ToList();
//if last item was selected count > 0
//else if last item was de-selected
//lastItemSelected.Count = -1
if (lastItemSelected.Count > 0)
{
lastItem.Name = lastItemSelected[0].ToString();
lastItem.SelectionType = "selected";
}//if
Console.WriteLine(lastItem.Name + " : " + lastItem.SelectionType);
//updated previousSelectedItems before returning
previousSelectedItemsList = currentSelectedItemsList;
}//getLastItemChanged
Version 2
(keeps track of last item - selected OR de-selected):
private void getLastItemChanged(ref ListBox lb)
{
List<string> lastItemSelected;
List<string> lastItemDeSelected;
List<string> currentSelectedItemsList = new List<string>();
//put selected items into a list
//so we can use list.Except
for (int i = 0; i < lb.SelectedItems.Count; i++)
{
//add selected items to list
currentSelectedItemsList.Add(lb.SelectedItems[i].ToString());
//Console.WriteLine("item: " + lb.SelectedItems[i]);
}//for
//get last item selected as a list
//by comparing the current list
//to the previous list
lastItemSelected = currentSelectedItemsList.Except(previousSelectedItemsList).ToList();
//if last item was selected count > 0
//else if last item was de-selected
//lastItemSelected.Count = -1
if (lastItemSelected.Count > 0)
{
lastItem.Name = lastItemSelected[0].ToString();
lastItem.SelectionType = "selected";
}//if
else
{
//if item was deselected, switch the order
//compare previous list to current list
lastItemDeSelected = previousSelectedItemsList.Except(currentSelectedItemsList).ToList();
//if > 0, last item was de-selected
if (lastItemDeSelected.Count > 0)
{
lastItem.Name = lastItemDeSelected[0].ToString();
lastItem.SelectionType = "de-selected";
}//if
}//else
Console.WriteLine(lastItem.Name + " : " + lastItem.SelectionType);
//updated previousSelectedItems before returning
previousSelectedItemsList = currentSelectedItemsList;
}//getLastItemChanged
Then in the ListBox SelectedValueChanged event handler (ex: listBox1_SelectedValueChanged), we call "getLastItemChanged":
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
getLastItemChanged(ref listBox1);
}
List.Except code adapted from here.
I re-worked the above code to create a version that allows you to specify if you want the last "Selected" item, the last "De-selected" item, or last item that was changed (either "Selected" or "De-selected").
Create a new class called "ItemInfo". Replace the code with the following:
ItemInfo:
public class ItemInfo
{
public string Name { get; set; }
public string SelectionType { get; set; }
}
In the form (Form1), create a list, a new instance of ItemInfo, and an enum as seen below:
public partial class Form1 : Form
{
//holds previously selected items
List<string> previousSelectedItemsList = new List<string>();
//holds the last selected and/or de-selected item
ItemInfo lastItem = new ItemInfo();
//create an enumeration
enum SelectionType { Deselected, Either, Selected };
public Form1()
{
InitializeComponent();
}
}
Now, we will create a method that will compare our lists and return an ItemInfo object:
private ItemInfo findExcludedItemInList(List<string> list1, List<string> list2)
{
ItemInfo item = new ItemInfo();
//get list of items in list1 that aren't in list2
var lastItemList1 = list1.Except(list2).ToList();
if (lastItemList1.Count > 0)
{
//there should only be 1 item in the list
item.Name = lastItemList1[0].ToString();
item.SelectionType = "Selected";
}//if
else
{
//get list of items in list2 that aren't in list1
var lastItemList2 = list2.Except(list1).ToList();
if (lastItemList2.Count > 0)
{
//there should only be 1 item in the list
item.Name = lastItemList2[0].ToString();
item.SelectionType = "Deselected";
}//if
}//else
return item;
}//findExcludedItemInList
Next, we will create a method "getLastItemChanged" that will use "findExcludedItemInList".
private void getLastItemChanged(ref ListBox lb, ref ItemInfo lastItem, ref List<string> previousSelectedItemsList, SelectionType sType)
{
List<string> currentSelectedItemsList = new List<string>();
//put selected items into a list
//so we can use list.Except
for (int i = 0; i < lb.SelectedItems.Count; i++)
{
currentSelectedItemsList.Add(lb.SelectedItems[i].ToString());
//Console.WriteLine("item: " + lb.SelectedItems[i]);
}//for
if (sType.Equals(SelectionType.Either))
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
Console.WriteLine(lastItem.Name + " : " + lastItem.SelectionType);
}//if
else if (sType.Equals(SelectionType.Selected))
{
//get last item selected as a list
//by comparing the current list
//to the previous list
if (String.Compare(findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList).SelectionType, "Selected", true) == 0)
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
Console.WriteLine(lastItem.Name + " : " + lastItem.SelectionType);
}//if
}//else if
else if (sType.Equals(SelectionType.Deselected))
{
//if item was deselected, switch the order
//compare previous list to current list
//if > 0, last item was de-selected
if (String.Compare(findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList).SelectionType, "Deselected", true) == 0)
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
Console.WriteLine(lastItem.Name + " : " + lastItem.SelectionType);
}//if
}//else if
//updated previousSelectedItems before returning
previousSelectedItemsList = currentSelectedItemsList;
}//getLastItemChanged
Then in the ListBox SelectedValueChanged event handler (ex: listBox1_SelectedValueChanged), we call "getLastItemChanged":
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
//get only last selected item
//getLastItemChanged(ref listBox1, ref lastItem, ref previousSelectedItemsList, SelectionType.Selected);
//get only last Deselected item
//getLastItemChanged(ref listBox1, ref lastItem, ref previousSelectedItemsList, SelectionType.Deselected);
//get last changed item - Selected or Deselected
getLastItemChanged(ref listBox1, ref lastItem, ref previousSelectedItemsList, SelectionType.Either);
Console.WriteLine("lastItem: " + lastItem.Name + " Type: " + lastItem.SelectionType);
}
There is no need to pass any of that stuff by reference, remove it all. You mix modes (windows controls writing to the console). Your methods have overly complicated signatures and almost all that code (if not all of it) should be inside a different class, not in your form. It is a ListBox monitor, code it as such.
Tough crowd. The Console.WriteLine statements were for debugging. I left them in so that one could verify the results. They should be removed when finished debugging.
Here is an updated version:
The concept of the code is the same. However, I moved the majority of the code to it's own class: ListBoxMonitor. "getLastItemChanged" now returns an "ItemInfo" object. I removed some parameters from "getLastItemChanged" and made them private variables in class ListBoxMonitor. I've also made ListBoxMonitor static.
Create a new class called "ListBoxMonitor.cs" and replace with the following code:
public static class ListBoxMonitor
{
//holds the last item that was changed
//based on SelectionType chosen by user
private static ItemInfo lastItem = new ItemInfo();
//holds the previously selected list items
private static List<string> previousSelectedItemsList = new List<string>();
//create an enumeration
public enum SelectionType { Deselected, Either, Selected };
public static ItemInfo getLastItemChanged(System.Windows.Forms.ListBox lb, SelectionType sType)
{
List<string> currentSelectedItemsList = new List<string>();
//put selected items into a list
//so we can use list.Except
for (int i = 0; i < lb.SelectedItems.Count; i++)
{
currentSelectedItemsList.Add(lb.SelectedItems[i].ToString());
}//for
if (sType.Equals(SelectionType.Either))
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
}//if
else if (sType.Equals(SelectionType.Selected))
{
//get last item selected as a list
//by comparing the current list
//to the previous list
if (String.Compare(findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList).SelectionType, "Selected", true) == 0)
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
}//if
}//else if
else if (sType.Equals(SelectionType.Deselected))
{
//if item was deselected, switch the order
//compare previous list to current list
//if > 0, last item was de-selected
if (String.Compare(findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList).SelectionType, "Deselected", true) == 0)
{
lastItem = findExcludedItemInList(currentSelectedItemsList,
previousSelectedItemsList);
}//if
}//else if
//updated previousSelectedItems before returning
previousSelectedItemsList = currentSelectedItemsList;
return lastItem;
}//getLastItemChanged
private static ItemInfo findExcludedItemInList(List<string> list1, List<string> list2)
{
ItemInfo item = new ItemInfo();
//get list of items in list1 that aren't in list2
var lastItemList1 = list1.Except(list2).ToList();
if (lastItemList1.Count > 0)
{
//there should only be 1 item in the list
item.Name = lastItemList1[0].ToString();
item.SelectionType = "Selected";
}//if
else
{
//get list of items in list2 that aren't in list1
var lastItemList2 = list2.Except(list1).ToList();
if (lastItemList2.Count > 0)
{
//there should only be 1 item in the list
item.Name = lastItemList2[0].ToString();
item.SelectionType = "Deselected";
}//if
}//else
return item;
}//findExcludedItemInList
}//class
Usage:
public partial class Form1 : Form
{
ItemInfo lastItem = null;
public Form1()
{
InitializeComponent();
}
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
//get only last selected item
//lastItem = ListBoxMonitor.getLastItemChanged(listBox1, ListBoxMonitor.SelectionType.Selected);
//get only last Deselected item
//lastItem = ListBoxMonitor.getLastItemChanged(listBox1, ListBoxMonitor.SelectionType.Deselected);
//get last changed item - Selected or Deselected
lastItem = ListBoxMonitor.getLastItemChanged(listBox1, ListBoxMonitor.SelectionType.Either);
}
}
I remembered that I can use CheckedBox instead.
Thanks all and I studied your code and learned new things.
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.