In a ComboBox there are some items which have to be enabled and some which have to be disabled (and visible).
At first the ComboBox ItemsSource is set:
comboBoxMachine.ItemsSource = machineList;
where comboBoxMachine is a ComboBox and machineList is a List<Machine> (Machine is a custom object)
Later a condition for each machine from the list has to be checked and in case it is fulfilled the appropriate item from the ComboBox has to be disabled.
Below is the combination of code/pseudocode of the logic:
private void modifyMachineComboBoxItems()
{
foreach (Machine mch in machineList)
{
if (constructionSiteSchedule.ReservationMachinePeriods.Count(x => x.MachineId == mch.Id) > 0) //if this condition is fulfilled, it should be not possible to select the machine from the comboBoxMachine
{
int currentPosition = machineList.IndexOf(mch);
disable the element from the comboBoxMachine at position currentPosition;
}
}
}
What I haven't figured out so far is how to disable the element from the ComboBox at given position so I would be very thankful if anyone could modify the code above such that the ComboBox items for machines that satisfy the condition are disabled.
You should just have boolean property on your machine that corresponds to whether the item is enabled, in your ComboBox.ItemContainerStyle you can bind IsEnabled to that property, then when you want to disable the item just set the property on the machine to false.
Alternatively you could use the ItemContainerGenerator (which you really should not):
var item = comboBoxMachine.ItemContainerGenerator.ContainerFromIndex(currentPosition) as ComboBoxItem;
item.IsEnabled = false;
(You do not need to get the position first by the way as there also is a ContainerFromItem method, also see the comment below)
Related
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 DataGridView on a form that is databound to a list of objects. I have several column including a checkbox column. The requirement is that only one item in the collection may have the databound boolean property set to true at a given time. The data object is named Interval, the Property in question is Program:
public bool Program
{
get { return _program; }
set
{
if (value)
{
Parent.Intervals.Except(new[] { this }).ForEach(interval => interval.Program = false);
}
_program = value;
OnPropertyChanged();
}
}
My expected behavior would be that clicking the databound checkbox would set the Program property for one instance of Interval to true, and in so doing, would set the Program property on all other instances to false. That is indeed what happens, however the datagridview does not update correctly. It will leave previous checkboxes checked and after tabbing off the cell or mousing over another checkbox, then it will update that particular checkbox.
How can I get the datagrid view to accurately show the state of my objects. I trusted you, DataGridView! You lied to me. I... I can never trust anything you say again.
The solution is to use a BindingList instead. When binding to a regular List or other collection (that is not of a Binding specific bent), only the selected rows values are updated. BindingList, however, has the added benefit that properties from all of the items are updated, not just the selected Row. Keep in mind that if you are using something like AddRange (from List) you may have to modify your code to add items either at initialization time or via a loop, as BindingList does not have an AddRange method (although an extension method could be written).
Try this
private void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if ((bool)dg.Rows[e.RowIndex].Cells[CheckBoxColumnIndex].Value)
{
var rows = (from DataGridViewRow row in Clients.Rows
where row.Index != e.RowIndex
select row).ToArray();
foreach(DataGridViewRow row in rows)
{
row.Cells[CheckBoxColumnIndex].Value = false;
}
}
}
Quite a simple question but I think its going to prove much harder than it sounds
I'd like to keep the selection of a listview item there when the focus leaves the list view, at the moment I've set the hideselection property to false and that's fine.. it does cause a VERY light gray selection to stay after the list view loses focus, so my question is, how can I properly show that that item is still selected so that the user will recognize that, something like changing the rows text colour or background colour? or just keeping it highlighted as when first selected the whole row turns blue?
I've had a look through intelisense and can't seem to find anything for a row or item or selected item's individual colour property?
It must exist though because selected items have their own background colour, where would I be able to change that?
Oh and the list view does need to stay in details view, which means I can't use the only method that I've been able to find whilst googling
thanks
Here's a solution for a ListView that does not allow multiple selections and
does not have images (e.g. checkboxes).
Set event handlers for the ListView (in this example it's named listView1):
DrawItem
Leave (invoked when the ListView's focus is lost)
Declare a global int variable (i.e. a member of the Form that contains the ListView,
in this example it's named gListView1LostFocusItem) and assign it the value -1
int gListView1LostFocusItem = -1;
Implement the event handlers as follows:
private void listView1_Leave(object sender, EventArgs e)
{
// Set the global int variable (gListView1LostFocusItem) to
// the index of the selected item that just lost focus
gListView1LostFocusItem = listView1.FocusedItem.Index;
}
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
// If this item is the selected item
if (e.Item.Selected)
{
// If the selected item just lost the focus
if (gListView1LostFocusItem == e.Item.Index)
{
// Set the colors to whatever you want (I would suggest
// something less intense than the colors used for the
// selected item when it has focus)
e.Item.ForeColor = Color.Black;
e.Item.BackColor = Color.LightBlue;
// Indicate that this action does not need to be performed
// again (until the next time the selected item loses focus)
gListView1LostFocusItem = -1;
}
else if (listView1.Focused) // If the selected item has focus
{
// Set the colors to the normal colors for a selected item
e.Item.ForeColor = SystemColors.HighlightText;
e.Item.BackColor = SystemColors.Highlight;
}
}
else
{
// Set the normal colors for items that are not selected
e.Item.ForeColor = listView1.ForeColor;
e.Item.BackColor = listView1.BackColor;
}
e.DrawBackground();
e.DrawText();
}
Note: This solution can result in some flicker. A fix for this involves subclassing the ListView control so you
can change the protected property DoubleBuffered to true.
public class ListViewEx : ListView
{
public ListViewEx() : base()
{
this.DoubleBuffered = true;
}
}
I created a class library of the above class so that I could add it to the toolbox.
A possible solution might be this answer to an another question:
How to change listview selected row backcolor even when focus on another control?
In .net 3.5 windows forms I have a listview with "CheckBoxes" = true. Is it possible to dim out or disable some items to prevent the user from checking the box?
You can use the ListBoxItem.ForeColor and UseItemStyleForSubItems properties to make the item look dimmed. Use SystemColors.GrayText to pick the theme color for disabled items. Avoid disabling selection, it prevents the user from using the keyboard. Only disable the checkbox checking. For example:
private void listView1_ItemCheck(object sender, ItemCheckEventArgs e) {
// Disable checking odd-numbered items
if (e.Index % 2 == 1) e.NewValue = e.CurrentValue;
}
You have to roll your own for this. Handle the ListView's ItemSelectionChanged event - if you don't want a particular item to be selectable, do this:
e.Item.Selected = false;
You can make a particular item appear unselectable by graying it out, changing the font color etc.
I took Hans Passant recommendation - good visual approach which in my case denotes un-actionable items.
Here's a sample:
'Select all attachements in case user wants to mask or pick and choose
For i As Integer = 0 To lstView.Items.Count - 1
If Not Scan.SupportedMasking.Contains(Path.GetExtension(lstView.Items(i).Text)) Then
lstView.Items(i).ForeColor = SystemColors.GrayText
lstView.Items(i).Text += " (No masking supported)"
lstView.Items(i).BackColor = SystemColors.InactiveBorder
lstView.Items(i).Selected = False
Else
lstView.Items(i).Selected = True
End If
Next i
use this or set the displaymode to view insted of edit!
public void SetItemEnabled(ListViewItem item, bool enabled)
{
if (item != null)
{
List<ListViewControl> lvControls = this.ListViewControls.FindAll(FindListViewControl(item));
foreach (ListViewControl lvControl in lvControls)
{
if (lvControl.Control != null)
{
lvControl.Control.Enabled = enabled;
}
}
}
}
You should set the AutoCheck property of the checkbox false.
AutoCheck - Gets or set a value indicating whether the Checked or CheckState values and the CheckBox's appearance are automatically changed when the CheckBox is clicked.
Actually this is usable only for the checkbox control.
Hey. I've got the following code that populates my list box
UsersListBox.DataSource = GrpList;
However, after the box is populated, the first item in the list is selected by default and the "selected index changed" event fires. How do I prevent the item from being selected right after the list box was populated, or how do I prevent the event from firing?
Thanks
To keep the event from firing, here are two options I have used in the past:
Unregister the event handler while setting the DataSource.
UsersListBox.SelectedIndexChanged -= UsersListBox_SelectedIndexChanged;
UsersListBox.DataSource = GrpList;
UsersListBox.SelectedIndex = -1; // This optional line keeps the first item from being selected.
UsersListBox.SelectedIndexChanged += UsersListBox_SelectedIndexChanged;
Create a boolean flag to ignore the event.
private bool ignoreSelectedIndexChanged;
private void UsersListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (ignoreSelectedIndexChanged) return;
...
}
...
ignoreSelectedIndexChanged = true;
UsersListBox.DataSource = GrpList;
UsersListBox.SelectedIndex = -1; // This optional line keeps the first item from being selected.
ignoreSelectedIndexChanged = false;
Well, it looks like that the first element is automatically selected after ListBox.DataSource is set. Other solutions are good, but they doesn't resolve the problem. This is how I did resolve the problem:
// Get the current selection mode
SelectionMode selectionMode = yourListBox.SelectionMode;
// Set the selection mode to none
yourListBox.SelectionMode = SelectionMode.None;
// Set a new DataSource
yourListBox.DataSource = yourList;
// Set back the original selection mode
yourListBox.SelectionMode = selectionMode;
i using the following, seems to work for me:
List<myClass> selectedItemsList = dataFromSomewhere
//Check if the selectedItemsList and listBox both contain items
if ((selectedItemsList.Count > 0) && (listBox.Items.Count > 0))
{
//If selectedItemsList does not contain the selected item at
//index 0 of the listBox then deselect it
if (!selectedItemsList.Contains(listBox.Items[0] as myClass))
{
//Detach the event so it is not called again when changing the selection
//otherwise you will get a Stack Overflow Exception
listBox.SelectedIndexChanged -= listBox_SelectedIndexChanged;
listBox.SetSelected(0, false);
listBox.SelectedIndexChanged += listBox_SelectedIndexChanged;
}
}
set IsSynchronizedWithCurrentItem="False" and Also SelectedIndex=-1 and every thing should work for you
If you just want to clear the selected value, you can use ClearSelected after you set the DataSource. But if you dont want the event to fire, then you'll have to use one of Joseph's methods.
Perhaps in DataSourceChanged you could check the state of SelectedIndex, if your lucky you could then just force SelectedIndex = -1.