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;
}
}
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 ...
}
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);
}
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 have a datagrid that is supposed to be an 'input table' for the end user to work with. (User provides data, clicks a button and program processes the data)
It is bound to an ObservableCollection (User class contains simple stuff such as email, name, login etc)
Everything works fine binding wise... but, I want to allow the users to correct their input, i.e. select a row and delete it from the grid, or clear contents of several cells etc - not all fields are mandatory.
However, when Del key is pressed, nothing happens. I tried handling the previewKeyDown event etc, but I got to the problem described below:
How I can Delete Selected Row in datagrid wpf?
Does this mean I cannot delete items when I am using binding? I have no idea how could I reliably modify the underlying collection - how can I know if I should remove the value of Name, Login, Email - and of which user in my collection?
I thought that two way binding would take care of this...
My code:
ItemsSource="{Binding ElementName=ThisUc,
Path=UsersToCreate,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
private ObservableCollection<User> _usersToCreate;
public ObservableCollection<User> UsersToCreate
{
get { return _usersToCreate ?? (_usersToCreate = new ObservableCollection<User>()); }
set
{
_usersToCreate = value;
RaisePropertyChanged("UsersToCreate");
ProgressBarMax = UsersToCreate.Count;
}
}
Cheers
Does this mean I cannot delete items when I am using binding?
Nope. It just means that if you're setting your items through ItemsSource, the DataGrid has no control over that collection (it could be any type of collection!) and relies on you to handle it.
I have no idea how could I reliably modify the underlying collection - how can I know if I should remove the value of Name, Login, Email - and of which user in my collection?
If you know the row, you know the user. The User object is the DataContext of the DataGridRow and its cells. You just have to remove that User from the UsersToCreate collection, and the DataGrid will update accordingly.
You could do this binding the SelectedItem property of the DataGrid, which will return the User object of the selected row.
As for clearing a given cell, it isn't that easy... Cells usually just represent properties of the data item of the row, the only way to clear its value is resetting that property to its default value. In this case, identifying the property is not simple.
The best way to clear a cell is using a DataGridTemplateColumn with some custom TextBox inside its template, that handles the PreviewKeyUp event and does a TextBox.Clear() when it detects the Del key is pressed and released.
Sure you can:
<DataGrid>
<DataGrid.InputBindings>
<KeyBinding Key="D" Command="{Binding DeleteCommand}" />
<KeyBinding Key="Delete" Command="{Binding DeleteCommand}" />
</DataGrid.InputBindings>
</DataGrid>
EDIT:
You can pass the selected item as parameter or do exactly as moncada suggests. For this to work I think the user have to select a row from the datagrid and not a cell, but not sure about that right now.
For future reference, this is my full solution:
private void UserDataGrid_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
var grid = sender as System.Windows.Controls.DataGrid;
if (e.Key == Key.Delete)
{
//treat each cell individually
foreach (var item in grid.SelectedCells)
{
User user = item.Item as User;
if (user == null)
continue;
//clear the property of the selected cell
foreach (var property in typeof(User).GetProperties())
{
if (property.Name == item.Column.Header.ToString())
{
property.SetValue(user, null, null);
}
}
//recreated the user object
User newUser = new User
{
Login = user.Login,
Address = user.Address,
Email = user.Email,
Name = user.Name,
Description = user.Description
};
//remove the old one and place the new one to update collection visually (otherwise DataGrid updates when only double-clicked)
var index = UsersToCreate.IndexOf(user);
UsersToCreate.Remove(user);
UsersToCreate.Insert(index, newUser);
}
//now check if any rows are completely empty - if so, then remove from DGV
List<int> indexesToRemove = new List<int>();
foreach (var user in UsersToCreate)
{
bool hasValue = false;
foreach (var property in typeof(User).GetProperties())
{
if (property.GetValue(user, null) != null)
{
hasValue = true;
}
}
if (!hasValue)
{
indexesToRemove.Add(UsersToCreate.IndexOf(user));
}
}
//go upside down to avoid index out of range (first remove last rows indexes)
for (int i = indexesToRemove.Count - 1; i >= 0; i--)
{
var index = indexesToRemove[i];
UsersToCreate.RemoveAt(index);
}
}
}
Although it's large, I think it's all pretty neat, because it allows to select several cells in several rows and clear them without reordering the datagrid etc. But also, it allows to remove the whole rows if all properties are null for the user.
Sadly, I need to readd the user, as when I simply update the properties, datagrid does not update visually - the collection is updated, but you need to dbl-click on the specific property cell to see that it's been wiped.
Thanks for help everybody
I have a combobox binded to a list like this
public List<CustomerLanguage> CurrentCustomerLanguageList
{
get { return _currentCustomerLanguageList; }
set
{
_currentCustomerLanguageList = value;
bsCustomerLanguages.DataSource = Presenter.CustomerLanguageToProxy(value);
cbLanguage.DataSource = bsCustomerLanguages.DataSource;
cbLanguage.DisplayMember = "LanguageName";
cbLanguage.ValueMember = "Id";
}
}
On the form i have + - buttons which must allow to add or delete items inside combobox.
The problem is: i dont know how to add new item to binding source and the list without full refresh of combobox.
Of course when i add, selected value must remains and no selectedvaluechanged event must be raised.
'bsCustomerLanguages' is a BindingSource ? In that case it should work if you set cbLanguage.DataSource = bsCustomerLanguages and add to bsCustomerLanguages directly