C# WinForms:
In some application like this:
I want to write a code for "Select All" button.
If I go and check which listview's "SelectedIndex" or "selected Item" property is greater than zero, then it won't work because what if user just has clicked inside the white area of them?
And also form.ActiveControl won't work either because when we click on "SelectAll" button, it is too late! ActiveControl is that SelectAll button.
maybe I could create a class level variable to remember which control has been clicked,etc..but I think there should be a better way....But what?!
Thanks
You could assign the 'GotFocus' event to a method like so, and record the 'last focused' control this way. Then in your SelectAll_CLick handler, if the listview is assigned, selectall, else - don't!
private ListView mLastSelectedListView;
private void ListViews_GotFocus(object sender, EventArgs e)'
{
ListView lv = sender as ListView;
if (null == lv) return;
mLastSelectedListView = lv;
}
private void SelectAll_Click(object sender, EventArgs e)
{
if (null == mLastSelectedListView) return;
mLastSelectedListView.SelectAll();
}
Here's a quick 'SelectAll' extension method to support the above;
public static class ListViewExtensions
{
public static void SelectAll(this ListView lv)
{
foreach (ListViewItem item in lv.Items)
item.Selected = true;
}
}
Related
I'd like to have a basic ListBox with associated Add and Delete buttons. The Delete button be enabled if and only if there is a selection in the ListBox, and the Add button should append an item to the end of the ListBox.
My first attempt was to just make a List<string> items field, like so:
public partial class Form1 : Form {
public Form1 {
InitializeComponent();
this.listBox1.DataSource = this.items;
}
private void addButton_Click(object sender, EventArgs e) {
this.items.Add("item {this.items.Count + 1}");
}
private void deleteButton_Click(object sender, EventArgs e) {
this.items.RemoveAt(this.listBox1.SelectedIndex);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e) {
this.deleteButton.enabled = (this.listBox1.SelectedIndex != -1);
}
List<string> items = new List<string>();
}
But, it turns out that the ListBox does not seem to update automatically as the items in the list change. Here's a related question about it.
Most advice suggested just running listBox1.DataSource = null; this.listBox1.DataSource = this.items; every time I updated the list, but that to me defeats the purpose of binding.
Another answer suggested using an ObservableCollection for my list's data type, but that did nothing different.
Finally I found the BindingList class and it seemed to do what I want; the ListBox automatically reflects the contents of the items field. (If there is another solution that is actually a best practice and not some piece of cargo cult folklore pasted around the Internet, please let me know.)
However, when the first item I add to items appears in the ListBox, the row becomes selected. And I can see that listBox1.SelectedIndex changes from -1 to 0. But, the listBox1_SelectedIndexChanged event does not fire. Hence, the state of the Delete button doesn't get updated properly.
(As an aside, this UI pattern is extremely commonplace, and it seems disastrous that there's so much conflicting information on implementing such a simple thing.)
I also ran into the same problem. Since this was the first question I came across, maybe my answer will save someone some time.
When DataSource is assigned an empty BindingList and items are added to that BindingList later, like so:
private BindingList<MyType> _myList = new BindingList<MyType>();
public MyForm()
{
listBox1.DataSource = _myList;
}
private void LoadData()
{
foreach (MyType item in GetDataFromSomewhere())
{
_myList.Add(item);
}
}
then after adding the first item, that will be selected in the ListBox, and listBox1.SelectedIndex will be 0 - but the SelectedIndexChanged event will not have been fired.
Looks like this could be a bug in ListBox as suggested here and here.
A workaround could be to derive from ListBox as Juan did, or just check if the BindingList contains any items before adding:
bool isFirstItem = (0 == _myList.Count);
_myList.Add(item);
if (isFirstItem)
{
// Your choice: set listBox1.SelectedIndex to -1 and back to 0,
// or call the event handler yourself.
}
Try manually selecting the last element, instead of relying on the default behavior. This will trigger the event.
public partial class Form1 : Form
{
BindingList<string> items = new BindingList<string>();
public Form1()
{
InitializeComponent();
this.listBox1.DataSource = items;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
this.deleteButton.Enabled = (this.listBox1.SelectedIndex != -1);
}
private void addButton_Click(object sender, EventArgs e)
{
this.items.Add($"item {this.items.Count + 1}");
this.listBox1.SelectedIndex = -1;
this.listBox1.SelectedIndex = this.listBox1.Items.Count - 1;
}
private void deleteButton_Click(object sender, EventArgs e)
{
this.items.RemoveAt(this.listBox1.SelectedIndex);
}
}
I'm trying to change an item on my RadContextMenu depending on the currently selected row in my RadGridView (edit: OrderList). I want the item to be enabled if the databound item in the current row has the correct property value.
The problem is that when I directly rightclick a row to open the RadContextmenu the CurrentRow has not yet been updated, so DropDownOpened is called with the old row.
If I left click or double right click it works fine.
Here's a bit of the code:
OrderMenu.DropDownOpened += OrderMenu_DropDownOpened;
And the method
private void OrderMenu_DropDownOpened(object sender, EventArgs e)
{
GoToParentOrderBtn.Enabled = GetSelectedOrder()?.ParentOrderId != null;
}
private OrderViewModel GetSelectedOrder()
{
return (OrderViewModel)OrderList.CurrentRow.DataBoundItem;
}
Use dataGridView.EndEdit(); This function commits and ends the edit operation on the current cell being edited.
More info here
Sorry for not specifying that I'm using a radgridview.
I found a related answer which helped me solve my problem.
I ended up making an extension (so I can use it all around the application) to RadGridView which fire an event on mousedown:
public partial class RadExtendedGridViewController : RadGridView
{
public RadExtendedGridViewController()
{
InitializeComponent();
base.MouseDown += RadExtendedGridViewController_MouseDown;
}
private void RadExtendedGridViewController_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var element = this.ElementTree.GetElementAtPoint(e.Location);
GridDataCellElement cell = element as GridDataCellElement;
if (cell?.RowElement is GridDataRowElement)
{
Rows[cell.RowIndex].IsSelected = true;
}
}
}
}
I then changed my GetSelectedOrder to using SelectedRows instead of Current:
private OrderViewModel GetSelectedOrder()
{
return (OrderViewModel)OrderList.SelectedRows.FirstOrDefault()?.DataBoundItem;
}
And now it works as intended. Thanks for taking your time trying to help me :-)
I have code using an AutoCompleteStringCollection:
private void txtS_TextChanged(object sender, EventArgs e)
{
TextBox t = sender as TextBox;
string[] arr = this.dbService.GetAll();
if (t != null)
{
if (t.Text.Length >= 3)
{
AutoCompleteStringCollection collection = new AutoCompleteStringCollection();
collection.AddRange(arr);
this.txtSerial.AutoCompleteCustomSource = collection;
}
}
}
How can I get the event for "item selected" after user selects an AutoComplete suggestion? And value of field?
There's no such thing as chosen item Event for a textBox, which I believe you're using for the AutoComplete. What you could do is add a key down event to your textBox. There you could verify if the enter key was pressed (clicking on a suggested link is the same as pressing enter). Something like that:
private void textBox1_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyData == Keys.Enter) {
String selItem = this.textBox1.Text;
}
}
Rather than focusing on detecting if an item from the autocomplete list was selected, instead you should check if the current value of the textbox is in the set of autocomplete entries.
if (txtSerial.AutoCompleteCustomSource.Contains(t.Text))
{
// Logic to handle an exact match being selected
...
}
else
{
// Update the autocomplete entries based on what was typed in
}
If the user typed in an exact string which happens to be be within the list of autocomplete values -- OR -- they select that value from the autocomplete list -- should this produce any different behavior? I think that in most cases it should not.
Short answer: make a custom event
Long answer:
You can intercept the KeyDown event of your textbox for numpad Enter or normal Enter and the mouse doubleclick event of the toolbox and compare the content of the toolbox then fire an event if they match that a delegate will pick up.
It depends a bit on the situation and workflow of your program but I have an example where I trigger the check on focuslost of the combobox. And then I check if the selected value is part of the collection:
private void cmbt1Name1_LostFocus(object sender, RoutedEventArgs e)
{
ComboBox cmb = sender as ComboBox;
FillFivePoints(cmb);
}
private void FillFivePoints(ComboBox usedCombobox)
{
if (txtSerial.AutoCompleteCustomSource.Contains(t.Text))
{
...
Is there any possibility to get a value of doubleclicked row in ListView?
I registered an event:
private void lvLista_DoubleClick(object sender, EventArgs e)
{
MessageBox.Show(lvLista.SelectedItems.ToString());
}
But on message, when i doubleclick some row in listview i get:
System.Windows.Forms.ListView+SelectedListViewItemCollection
What is more, I have got 2 columns in listview:
lvLista.Columns.Add("ID");
lvLista.Columns.Add("Tilte");
And i want to show in messagebox the "ID" of doubleclicked row.
How to do it? How to get a values from this event?
If you handle the MouseDown and/or MouseDoubleClick events of the ListView control, and use the HitTest method to determine the target of the mouse action, you will know which item has been double clicked. This is also a good means to determine if NO item was clicked (for example, clicking on the empty area in a partially filled list.
The following code will display the clicked item in a textbox if a single click occurs, and will pop up a message box with the name of the double-clicked item if a double click occurs.
If the click or double click occur in an area of the list view not populated by an item, the text box or message box inform yopu of that fact.
This is a trivial example, and depending on your needs, you will have to mess with it a little.
UPDATE: I added some code which clears the SelectedItems property of the Listview control when an empty area of the list is clicked or double-clicked.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
listView1.MouseDown += new MouseEventHandler(listView1_MouseDown);
listView1.MouseDoubleClick += new MouseEventHandler(listView1_MouseDoubleClick);
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
this.SetupListview();
}
private void SetupListview()
{
ListView lv = this.listView1;
lv.View = View.List;
lv.Items.Add("John Lennon");
lv.Items.Add("Paul McCartney");
lv.Items.Add("George Harrison");
lv.Items.Add("Richard Starkey");
}
void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
ListViewHitTestInfo info = listView1.HitTest(e.X, e.Y);
ListViewItem item = info.Item;
if (item != null)
{
MessageBox.Show("The selected Item Name is: " + item.Text);
}
else
{
this.listView1.SelectedItems.Clear();
MessageBox.Show("No Item is selected");
}
}
void listView1_MouseDown(object sender, MouseEventArgs e)
{
ListViewHitTestInfo info = listView1.HitTest(e.X, e.Y);
ListViewItem item = info.Item;
if (item != null)
{
this.textBox1.Text = item.Text;
}
else
{
this.listView1.SelectedItems.Clear();
this.textBox1.Text = "No Item is Selected";
}
}
}
Try this:
private void lvLista_DoubleClick(object sender, EventArgs e)
{
MessageBox.Show(lvLista.SelectedItems[0].SubItems[0].Text);
}
I know this thread is old but nobody here answered the question properly in my opinion. For those in the future, try this, from MSDN:
// User must double-click to activate item
myListView.Activation = System.Windows.Forms.ItemActivation.Standard;
// Add event handler
myListView.ItemActivate += new
System.EventHandler(this.myListView_ItemClick);
Since the accepted answer didn't help me i thought that I would share my solution to the same problem: getting data from a specific column in a listview in the double click event.
The following line returns the data of the second column in the row that I've double clicked on as a string:
private void listViewOutput_DoubleClick(object sender, EventArgs e)
{
string content = listViewOutput.Items[listViewOutput.SelectedIndices[0]].SubItems[1].Text
}
Thanks; this is what I needed. I thought I'd also mention one could set up the local info variable more generally as:
ListViewHitTestInfo info = ((ListView)sender).HitTest(e.X, e.Y);
Try this
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
ListViewHitTestInfo hit = listView1.HitTest(e.Location);
Rectangle rowBounds = hit.SubItem.Bounds;
Rectangle labelBounds = hit.Item.GetBounds(ItemBoundsPortion.Label);
int leftMargin = labelBounds.Left - 1;
string x = hit.Item.Text;
}
After I set "EditOnEnter" to be true, the DataGridViewComboBoxCell still takes two clicks to open if I don't click on the down arrow part of the combo box.
Anyone have any clue how to fix this? I've got my own DataGridView class that I use, so I can easily fix this issue system-wide with a few smart event handlers I hope.
Thanks.
Since you already have the DataGridView's EditMode property set to "EditOnEnter", you can just override its OnEditingControlShowing method to make sure the drop-down list is shown as soon as a combo box receives focus:
public class myDataGridView : DataGridView
{
protected override void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e)
{
base.OnEditingControlShowing(e);
if (e.Control is ComboBox) {
SendKeys.Send("{F4}");
}
}
}
Whenever an edit control in your DataGridView control gets the input focus, the above code checks to see if it is a combo box. If so, it virtually "presses" the F4 key, which causes the drop-down portion to expand (try it when any combo box has the focus!). It's a little bit of a hack, but it works like a charm.
I used this solution as it avoids sending keystrokes:
Override the OnCellClick method (if you're subclassing) or subscribe to the CellClick event (if you're altering the DGV from another object rather than as a subclass).
protected override void OnCellClick(DataGridViewCellEventArgs e)
{
// Normally the user would need to click a combo box cell once to
// activate it and then again to drop the list down--this is annoying for
// our purposes so let the user activate the drop-down with a single click.
if (e.ColumnIndex == this.Columns["YourDropDownColumnName"].Index
&& e.RowIndex >= 0
&& e.RowIndex <= this.Rows.Count)
{
this.CurrentCell = this[e.ColumnIndex, e.RowIndex];
this.BeginEdit(false);
ComboBox comboBox = this.EditingControl as ComboBox;
if (comboBox != null)
{
comboBox.DroppedDown = true;
}
}
base.OnCellContentClick(e);
}
protected override void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e)
{
base.OnEditingControlShowing(e);
DataGridViewComboBoxEditingControl dataGridViewComboBoxEditingControl = e.Control as DataGridViewComboBoxEditingControl;
if (dataGridViewComboBoxEditingControl != null)
{
dataGridViewComboBoxEditingControl.GotFocus += this.DataGridViewComboBoxEditingControl_GotFocus;
dataGridViewComboBoxEditingControl.Disposed += this.DataGridViewComboBoxEditingControl_Disposed;
}
}
private void DataGridViewComboBoxEditingControl_GotFocus(object sender, EventArgs e)
{
ComboBox comboBox = sender as ComboBox;
if (comboBox != null)
{
if (!comboBox.DroppedDown)
{
comboBox.DroppedDown = true;
}
}
}
private void DataGridViewComboBoxEditingControl_Disposed(object sender, EventArgs e)
{
Control control = sender as Control;
if (control != null)
{
control.GotFocus -= this.DataGridViewComboBoxEditingControl_GotFocus;
control.Disposed -= this.DataGridViewComboBoxEditingControl_Disposed;
}
}
To avoid the SendKeys issues, try the solution from Open dropdown(in a datagrid view) items on a single click. Essentially, in OnEditingControlShowing hook to the Enter event of the combo box, in the Enter event handler, set ComboBox.DroppedDown = true. That seems to have the same effect, but without the side effects #Cody Gray mentions.