I've got a ListBox with a bunch of items in it. The user can click an item to edit its contents. How do I prevent the user from deselecting all items? i.e., the user shouldn't be able to have nothing selected.
There is a case missing in your situation, which is when the list is cleared you will reselect an item there is no longer on the list. I solve this by adding an extra check.
var listbox = ((ListBox)sender);
if (listbox.SelectedItem == null)
{
if (e.RemovedItems.Count > 0)
{
object itemToReselect = e.RemovedItems[0];
if (listbox.Items.Contains(itemToReselect))
{
listbox.SelectedItem = itemToReselect;
}
}
}
I then put this inside a behaviour.
I'm not sure if there is a direct way to disable deselecting an Item, but one way which would be transparent to the user is to keep track of the last selected Item, and whenever the SelectionChanged event is raised and the selected index is -1, then reselect the last value.
This Works for Sure to Prevent User from Deselect... Add those 2 Events to your checkedListBox1 and set the Property CheckOnClick to "True" in Design Mode. (MSVS2015)
private void checkedListBox1_SelectedValueChanged(object sender, EventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
private void checkedListBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
To disable on or more options in your listbox/dropdown, you can add the "disabled" attribute as shown below. This prevent the user from selection this option, and it gets a gray overlay.
ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);
One solution, as suggested by amccormack:
private void hostsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(lstHosts.SelectedItem == null)
{
if(e.RemovedItems.Count > 0)
{
lstHosts.SelectedItem = e.RemovedItems[0];
}
Related
Hy!
I would like to create an ObjectListView, where you can delete items with a ContextMenu.
So basicly I used to delete it by getting OLV.SelectedIndex, and then deleting from the list OLV is based on, and re-setting the OLV objects.
Then I realized, if I sort the OLV, then delete an item, it deletes another item, since the selected item index does not equals the index in the list.
With OLV CellRightClick event I can get the object behind the clicked item (e.Model),but I dont know how to pass it to the ContextMenu click event handler.
Subjects is a List.
private void subjectListView_CellRightClick(object sender, BrightIdeasSoftware.CellRightClickEventArgs e)
{
if (subjectsListView.SelectedIndex != -1)
{
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("Delete", new EventHandler(DeleteItem));
subjectsListView.ContextMenu = cm;
}
}
void DeleteItem(object sender, EventArgs e)
{
//get the Subject object, which was clicked on
Subjects.RemoveAt(subjectsListView.SelectedIndex);
subjectsListView.SetObjects(Subjects);
}
So basically I want to get the object (not the index) when the ContextMenus "Delete" item is clicked.
Also, I feel like there is an easier way to do this.
Thanks for the answer.
I would just assign an appropriate ContextMenuStrip from the designer to the ObjectListView.ContextMenuStrip property and then handle the click of the corresponding "Delete" click like this:
private void deleteToolStripMenuItem_Click(object sender, EventArgs e) {
if (objectListView1.SelectedObject != null) {
objectListView1.RemoveObject(objectListView1.SelectedObject);
}
}
Or is there a requirement I am missing from your question?
I have a winform with a group of comboboxes, all of the comboboxes with the same list items in them.
I need a way to confirm that when the user is done selecting a value in each box that they only selected each list value once.
Ex:
cbox1 cbox2 cbox 3
Item A Item B Item A (this needs to flag an error since Item A is already selected in cbox1)
I was thinking trying to use the selectedvaluecommited action (as after i populate the list I change the selected index to -1 so they all show "empty" to start) but the loop to make it work seems to be eluding me.
background: this is choosing fields to build a spreadsheet and the user needs to choose the field order.
You can do it like this (quick and dirty):
Add SelectedIndexChanged handler for all three comboboxes (in Form_Load in example)
comboBox1.SelectedIndexChanged += CheckComboBoxes;
comboBox2.SelectedIndexChanged += CheckComboBoxes;
comboBox3.SelectedIndexChanged += CheckComboBoxes;
in CheckComboBoxes method do your checking:
private void CheckComboBoxes(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == comboBox2.SelectedIndex ||
comboBox1.SelectedIndex == comboBox3.SelectedIndex ||
comboBox2.SelectedIndex == comboBox3.SelectedIndex)
MessageBox.Show("comboboxes are not unique");
}
EDIT:
this is approach when having n comboboxes. Put all items into list, select distinct values and compare that distinct count with items count... Something like this:
private void CheckComboBoxes(object sender, EventArgs e)
{
List<string> comboValues = new List<string>();
foreach (Control c in this.Controls)
{
if (c is ComboBox && !string.IsNullOrEmpty((c as ComboBox).SelectedItem.ToString()))
comboValues.Add((c as ComboBox).SelectedItem.ToString());
}
if (comboValues.Distinct().ToList().Count < comboValues.Count)
MessageBox.Show("not all combos are unique");
}
Here's an approach you can take.
To make the affected comboboxes easy to distinguish, put them all in a GroupBox container.
Write a validation method for your group box.
Subscribe to the group box Validating event by attaching it to your validation method.
In your validation method, loop through all the ComboBox controls in the group box and check if there are any duplicates, and issue an error if so.
For example, assuming the group box is called groupBox1:
private void GroupBox1_Validating(object sender, CancelEventArgs e)
{
base.OnValidating(e);
var selectedIndices = groupBox1.Controls.OfType<ComboBox>().Select(item => item.SelectedIndex);
var anyDuplicates = selectedIndices.GroupBy(x => x).Any(x => x.Count() > 1);
if (!anyDuplicates)
return;
MessageBox.Show("There are duplicates!");
e.Cancel = true;
}
And subscribe to the group box Validating event in the Form1 constructor:
public Form1()
{
InitializeComponent();
groupBox1.Validating += GroupBox1_Validating;
}
Sometimes when validating like this, you need to prevent the validation logic from executing if the user clicks the Cancel button. You're supposed to be able to set the CausesValidation property of the Cancel button to false to prevent this, but I find that it doesn't work for me.
Instead, I just use a bool cancelling field which I set to true in the Cancel button handler:
private void cancelButton_Click(object sender, EventArgs e)
{
cancelling = true;
this.Close();
}
bool cancelling;
And then add the following to the start of GroupBox1_Validating():
if (cancelling)
return;
If it is possible to have different UI design then my suggestion goes as under:
Alternative UI Design - A
Create One ListBox ListFieldsOriginal and populate
Create Second ListBox ListUserSelection, keep it empty initially
Provide buttons as under:
Button '>' means add currently selected item from ListFieldsOrginial to ListUserSelection at end; and remove that item from ListFieldsOriginal
Button '<' means remove currenly selected item from lstUserSelection; and add that item back to ListFieldsOriginal (of course at end)
NOTE: If adding item back to ListFieldsOriginal is your requirement then extra coding is required to find its appropriate index in the ListFieldsOriginal.
Alternative UI Design - B
Create One CheckedListBox ListFieldsOriginal and populate
Create one ListBox ListUserSelection, keep it empty initially
Define ItemCheck event handler for ListFieldsOriginal to add/remove items to/from ListUserSelected.
if (e.CurrentValue==CheckState.Unchecked)
{
string item = ListFieldsOriginal.Items[item];
ListUserSelection.Items.Add(item);
}
else
{
string item = ListFieldsOriginal.Items[item];
ListUserSelection.Items.Remove(item);
}
In the UI I'm working with, in a DataGrid, there are cases where the user might select a different row, but after a dialog interaction, the old row needs to be shown as being selected once again. If I simply try
BundleQueueDG.SelectedIndex = currentBundleIndex;
that does not do anything and in fact, once the SelectionChanged method exits, it changes to the new value. What is the best way to "re-select" the previously-selected row?
Try setting the SelectedItem property. Preserve what was selected before and set the SelectedItem with what was previously selected in your event.
Something like this:
private void DgDataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
int newIndex = (sender as DataGrid).SelectedIndex / 2;
if (Convert.ToInt32(newIndex) >= 1)
(sender as DataGrid).SelectedItem = previous;
else
{
previous = (sender as DataGrid).CurrentItem;
}
}
I have a datagrid that I fill with data from a database.
When I click on a row, I call the GotFocus method and try to make a button visible if certain requirements are met.
private void dtgVerkoopsdocumenten_GotFocus(object sender, RoutedEventArgs e)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
if (row.soort2 == "Factuur")
{
btnBoeking.IsHitTestVisible = true;
btnBoeking.Opacity = 1;
}
else
{
btnBoeking.IsHitTestVisible = false;
btnBoeking.Opacity = 0.5;
}
}
This gives me an error.
Index was out of range. Must be non-negative and less than the size of the collection.
Now when I call the code but from a button click it does it how it's supposed to work.
private void tester_Click(object sender, RoutedEventArgs e)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
test.Content = row.soort2;
if (row.soort2 == "Factuur")
{
btnBoeking.IsHitTestVisible = true;
btnBoeking.Opacity = 1;
}
else
{
btnBoeking.IsHitTestVisible = false;
btnBoeking.Opacity = 0.5;
}
}
Why is this?
Why dont you use DataGrid SelectedIndexChanged event?
Wyy use GotFocus that doesnt tell you if user even made a selection to start with,
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
Called from gotfocus will fail as you have nothing selected besides having no error check in place to check if selection,
If you use Selection changed events you know the user has made selection changes there are a number of events for selection
before access selected items by index you need to check selected item count is grater than zero condition.
Because dtgVerkoopsdocumenten.SelectedItems are empty and GotFocus event raise before SelectedItemChanged event so we are not sure the dtgVerkoopsdocumenten.SelectedItems have any item or not.
You can check dtgVerkoopsdocumenten.SelectedItems before do anything.
if (dtgVerkoopsdocumenten.SelectedItems != null &&
dtgVerkoopsdocumenten.SelectedItems.Count > 0)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
...
}
I want to select item in a ListView upon clicking. I also want to know what I clicked.
I work on winforms with c#.I also want to know How I can clicking the all row?
Just handle the Click event on the list and use the ListView.SelectedItems property to get what items are selected:
private void listView1_Click(object sender, EventArgs e)
{
var firstSelectedItem = listView1.SelectedItems[0];
}
u can use MouseEventArgs and get the mouse location check if it exists inside the selected item bound , that means the click was made on the selected item .
EDIT :
example :
private void myList_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (myList.SelectedItems.Count >= 1)
{
ListViewItem item = myList.SelectedItems[0];
//here i check for the Mouse pointer location on click if its contained
// in the actual selected item's bounds or not .
// cuz i ran into a problem with the ui once because of that ..
if (item.Bounds.Contains(e.Location))
{
MessageBox.Show("Double Clicked on :"+item.Text);
}
}
}
also if you use xaml for window then you must add MouseUp="listView1_Click" attribute to ListView tag