So, whenever I change the selections I need to call a method, that compares the new selection with different options. Problem is, that it always sends the object selected before that
Initially I thought I could just invert the selection, but that would only work with 2 options.
// Create the Combobox
ComboBox selectType = new ComboBox();
selectType.Text = "Select Type";
selectType.SelectionChanged += CallChange;
ComboBoxItem sortingAlgorithm = new ComboBoxItem();
sortingAlgorithm.Content = "Sorting Algorithm";
ComboBoxItem searchingAlgorithm = new ComboBoxItem();
searchingAlgorithm.Content = "Searching Algorithm";
// add the items to ComboBox
// Call on new selection
void CallChange(object sender, SelectionChangedEventArgs args)
{
_controller.ChangeType((string)selectType.SelectionBoxItem);
}
I'd think that it just sends the new selection. Do I have any thinking mistakes or did I mix anything up? Also I know that using Strings to compare selections is very bad practice, I am currently changing it all to dictionaries
The selected item is only propagated after the changed event was handled. This allows to manipulate the selected value before it is visible as selected. Therefore the moment the SelectionChanged event occurred, the SelectionBoxItem hasn't changed yet. You have to reference the selected item from the args object instead:
// Call on new selection
void CallChange(object sender, SelectionChangedEventArgs args)
{
_controller.ChangeType(args.AddedItems.OfType<string>().FirstOrDefault() ?? string.Empty);
}
Related
I got a problem with a ListBox in a WinForm application. I have two ListBoxes inside of a tab control and depending on the selection in the first one (lb1), the DataSource of the second one (lb2) changes. This is done in the SelectedValueChanged Event.
private void listBox_ControlUnits_SelectedValueChanged(object sender, EventArgs e)
{
ControlUnit unit = (sender as ListBox).SelectedItem as ControlUnit;
textBox_ProjectNameTab.Text = unit.ProjectName;
listBox_ControlCircuits.DataSource = null;
listBox_ControlCircuits.DataSource = unit.ControlCircuits;
}
lb1 is filled with a DataSource, too.
Now if I select a value in lb1 the selection automatically jumps back to the first item and I can not figure out why. is this some kind of UI update problem?
Even without the SelectedValueChanged event and the connection to the second listbox the issue occures.
Short gif of the problem, sorry for the blurriness
If I select one item more than once it works somehow (as seen in the gif).
Edit:
I found the problem but I do not quite understand what happens.
I have another listBox on another tab of my tab control. This listBox has the same DataSource as lb1. This seems to cause this behavior.
I finally found the problem:
I did not know that if I use the same DataSource for two ListBoxes they share the BindingContext per default.
I created a new BindingContext for the second ListBox and now the selection does no longer change.
listBox_allGroups.DataSource = null;
listBox_allGroups.DataSource = x.y;
listBox_allGroups.DisplayMember = "Name";
listBox_ControlUnits.DataSource = null;
listBox_ControlUnits.DataSource = x.y;
listBox_ControlUnits.DisplayMember = "Name";
listBox_ControlUnits.BindingContext = new BindingContext();
You can use a variable to hold the selected item
object _selecteditem=null;
and check it in ListBox click event.
prive void ListBox1_Click(object sender,EventArgs e)
{
if(ListBox1.SelectItem == _selecteditem) return;
// do ...
}
I have five RadioButton and five ComboBox controls.
Each RadioButton is connected to a ComboBox.
When I activate one RadioButton, the corresponding ComboBox, it gets enabled.
Now when I choose another RadioButton, the information in the previously selected ComboBox should clear but does not!
I have tried with ComboBox.Clear() as well as ComboBox.Reset(), but it doesn't work.
Here is my code for one of the ComboBox and RadioButton
if (radioButtondinner.Checked == true)
{
comboBoxdinner.DataSource = DList.Dwork();
comboBoxdinner.DisplayMember = "dinner";
}
As I said in comment: you can use one Combobox and only to change data sources when you check other RadioButton that should work sure
But If you want to have more Combobox then just type in else statements
comboBox.DataSource = null;
// create a check change event and use this.
private void radioButtondinner_CheckedChanged(object sender, EventArgs e)
{
if (!radioButtondinner.Checked)
{
// if you want to clear only the text or selected item text
comboBoxdinner.Text = String.Empty;
// if you want to clear the entire data source
comboBoxdinner.DataSource = null;
}
}
I found that Items.Clear does not always clear a listbox when the listbox has been filled via a DataSource. Setting the DataSource to Null allows it to be cleared with Items.Clear().
Is this the wrong way to do it this way? Is my thinking a bit wrong to do this?
Thanks.
Below is the code I prepared to illustrate my problem. It includes one Listbox and three buttons.
If you click the buttons in this order everything Everything works:
Fill List With Array button
Fill List Items With Array button
Fill List Items With DataSource button
But if you click the "Fill List Items With DataSource" button first, clicking on either of the other two buttons causes this error: "An unhandled exception of type 'System.ArgumentException' occurred in System.Windows.Forms.dll" with "Items collection cannot be modified when the DataSource property is set."
Comments?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnFillListWithArray_Click(object sender, EventArgs e)
{
string[] myList = new string[4];
myList[0] = "One";
myList[1] = "Two";
myList[2] = "Three";
myList[3] = "Four";
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.Items.AddRange(myList);
}
private void btnFillListItemsWithList_Click(object sender, EventArgs e)
{
List<string> LStrings = new List<string> { "Lorem", "ipsum", "dolor", "sit" };
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.Items.AddRange(LStrings.ToArray());
}
private void btnFillListItemsWithDataSource_Click(object sender, EventArgs e)
{
List<string> LWords = new List<string> { "Alpha", "Beta", "Gamma", "Delta" };
//listBox1.DataSource = null; <= required to clear list
listBox1.Items.Clear();
listBox1.DataSource = LWords;
}
}
According to Microsoft it looks like setting the Datasource to Null then Clearing the list is acceptable.
Source: http://support.microsoft.com/kb/319927
If your listbox is bound to a datasource, then that datasource becomes the 'master' of the listbox. You then don't clear the listbox, but you need to clear the datasource.
So if the listbox is bound to LWords, you do Lwords.clear() and the listbox would be cleared.
And that is correct behaviour, because that is what being databound is all about.
If you set the datasource to null, you are basically telling the listbox that it is no longer databound. And of course as a side effect of that it becomes empty.
But depending on the situation you might not want the listbox just to be cleared, but you might want to clear the datasource and the listbox both.
Suppose you want to clear LWords via your GUI, and that LWords is the source of your listbox, you press a button and you set the datasource to null, you see the listbox becoming empty, thinking that LWords is not empty, but LWords is not empty at all, and then in this situation that would be a bug.
I have a databound combobox:
using(DataContext db = new DataContext())
{
var ds = db.Managers.Select(q=> new { q.ManagerName, q.ManagerID});
cmbbx_Managers.BindingContext = new BindingContext();
cmbbx_Managers.DataSource = ds;
cmbbx_Managers.DisplayMember = "ManagerName";
cmbbx_Managers.ValueMember = "ManagerID";
}
When the form loads neither item is selected, but when the user chooses an item it cannot be deselected. I tried to add cmbbx_Managers.items.Insert(0, "none"), but it does not solve the problem, because it is impossible to add a new item to the databound combobox.
How do I allow a user to deselect a combobox item?
To add an item to your databound ComboBox, you need to add your item to your list which is being bound to your ComboBox.
var managers = managerRepository.GetAll();
managers.Insert(0, new Manager() { ManagerID = 0, ManagerName = "(None)");
managersComboBox.DisplayMember = "ManagerName";
managersComboBox.ValueMember = "ManagerID";
managersComboBox.DataSource = managers;
So, to deselect, you now simply need to set the ComboBox.SelectedIndex = 0, or else, use the BindingSource.CurrencyManager.
Also, one needs to set the DataSource property in last line per this precision brought to us by #RamonAroujo from his comment. I updated my answer accordingly.
The way you "deselect" an item in a drop-down ComboBox is by selecting a different item.
There is no "deselect" option for a ComboBox—something always has to be selected. If you want to simulate the behavior where nothing is selected, you'll need to add a <none> item (or equivalent) to the ComboBox. The user can then select this option when they want to "deselect".
It is poor design that, by default, a ComboBox appears without any item selected, since the user can never recreate that state. You should never allow this to happen. In the control's (or parent form's) initializer, always set the ComboBox to a default value.
If you really need a widget that allows clearing the current selection, then you should use a ListView or ListBox control instead.
To deselect an item suppose the user presses the Esc key, you could subscribe to the comboxBox KeyDown event and set selected index to none.
private void cmbbx_Managers_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape && !this.cmbbx_Managers.DroppedDown)
{
this.cmbbx_Managers.SelectedIndex = -1;
}
}
I have a ListView which displays multiple rows of ListViewItems. The user is able to edit one of these items through a dialog which opens after clicking 'Edit.' When the dialog closes I would like to modify the selected ListViewItem such that it reflects the new settings.
Here is how I currently update my item:
private void btnEditSnmpV3Setting_Click(object sender, EventArgs e)
{
if (lstVwSNMPv3Settings.SelectedItems.Count > 0)
{
ListViewItem selectedItem = lstVwSNMPv3Settings.SelectedItems[0];
NetworkDiscoverySnmpSetting settings = (NetworkDiscoverySnmpSetting)selectedItem.Tag;
NetworkDiscoverySnmpV3SettingsDialog dialog = new NetworkDiscoverySnmpV3SettingsDialog(settings);
//Pass in the owner for centering of dialog.
if (dialog.ShowDialog(this) == DialogResult.OK)
{
selectedItem.SubItems.Clear();
selectedItem.Text = settings.SnmpV3Username;
selectedItem.SubItems.Add(settings.SecurityMode.ToString());
selectedItem.SubItems.Add(settings.AuthenticationProtocol.ToString());
selectedItem.SubItems.Add(settings.PrivacyProtocol.ToString());
selectedItem.Tag = settings;
}
}
}
I found this to be a poor solution due to the fact that I need to touch code in multiple places if my ListView's number of columns changes.
I handled this code-reuse issue during the 'Add' event (as opposed to 'Edit') by giving NetworkDiscoverySnmpSetting a utility method:
public ListViewItem ToListViewItem()
{
ListViewItem listViewItem = new ListViewItem();
listViewItem.Text = SnmpV3Username;
listViewItem.SubItems.Add(SecurityMode.ToString());
listViewItem.SubItems.Add(AuthenticationProtocol.ToString());
listViewItem.SubItems.Add(PrivacyProtocol.ToString());
listViewItem.Tag = this;
return listViewItem;
}
which is used like so:
private void btnAddSnmpV3Setting_Click(object sender, EventArgs e)
{
NetworkDiscoverySnmpSetting settings = new NetworkDiscoverySnmpSetting(NetworkDiscovery.ID);
NetworkDiscoverySnmpV3SettingsDialog dialog = new NetworkDiscoverySnmpV3SettingsDialog(settings);
//Pass in the owner for centering of dialog.
if (dialog.ShowDialog(this) == DialogResult.OK)
lstVwSNMPv3Settings.Items.Add(settings.ToListViewItem());
}
Unfortunately, ListView.SelectedItems does not allow collection-modification. As such, this does not compile:
lstVwSNMPv3Settings.SelectedItems[0] = settings.ToListViewItem();
How should I change my first code-snippet so that I do not need to update my code in multiple places when ListView's columns change?
You can modify the element itself rather than replacing it with another one, because ListViewItem is a class, so it's a reference type.
In order to do this follow these steps:
get currently selected item and save it to variable like this: ListViewItem selectedItem = lstVwSNMPv3Settings.SelectedItems[0];
modify your ToListViewItem method to void ToListViewItem(ListViewItem listViewItem) (return void and take ListViewItem object as parameter and modify it instead of creating a new object. It should also rather modify properties of existing subitems than creating new ones. It can look more or less like this:
public void ToListViewItem(ListViewItem listViewItem)
{
listViewItem.Text = SnmpV3Username;
listViewItem.SubItems[0].Text = SecurityMode.ToString();
listViewItem.SubItems[1].Text = AuthenticationProtocol.ToString();
listViewItem.SubItems[2].Text = PrivacyProtocol.ToString();
listViewItem.Tag = this;
}
call ToListViewItem(selectedItem);
you don't have to assign the modified item back to the collection, because you use a reference, which means you've just modify the same object that's in the ListView
I did a quick test and the method seems to modify texts of existing items without issues.
ListViewItems have a bool Selected property that you can toggle to make them selected or not selected.
A much simpler solution, that worked for me:
lstVwSNMPv3Settings.Items[lstVwSNMPv3Settings.SelectedIndices[0]] = myNewItem;
But be careful to first make sure there is an item selected:
if (lstVwSNMPv3Settings.SelectedIndices.Count > 0) { ... }