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 :-)
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 developing a C# Windows Form program and I've implemented a DataGridView on it. Now, after setting the data source, when I click the top left button on the datagridview, it selects all rows, just like Microsoft Excel. However, I dynamically hide and show rows on it, and after clicking that button I realized that it also selects the invisible ones. I don't want to implement "SelectionChanged" event because I constantly select some rows and normally I can't select the invisible ones. Only this button selects it. I'm looking for an event like this:
datagridView1_SelectAllClicked(object sender, EventArgs e)
{
// do stuff
}
Something like this will also work since I don't have to check all selections:
dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if(dataGridView1.IsSelectAllCells())
{
// do stuff
}
}
If I have to, I will add the event to deselect the invisible rows, but I prefer some solution like the first one. Any advices? Thanks in advance.
Edit: I'm checking "dataGridView1.SelectedRows" property on button clicks only, not after the selection was made. So, some function that I can implement to button click events will also solve my problem.
The DataGridView class provides the AreAllCellsSelected methode:
Returns a value indicating whether all the DataGridView cells are currently selected. (MSDN)
With that we can get a solution like your second one:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
DataGridView view = sender as DataGridView;
if (view.AreAllCellsSelected(true))
{
foreach (DataGridViewRow row in view.Rows)
{
//deselect all invisible rows
if (!row.Visible)
row.Selected = false;
}
}
}
I've managed to solve it finally, the solution was simpler than I expected.
private void RemoveInvisibleSelection()
{
if (dataGridView1.SelectedRows.Count == dataGridView1.Rows.Count)
{
for (int i = 0; i < dataGridView1.SelectedRows.Count; i++)
if (!dataGridView1.SelectedRows[i].Visible)
dataGridView1.SelectedRows[i--].Selected = false; // decreased the index value since SelectedRows property loses an object
}
}
I have a unbound DataGridView with 6 columns, the first being a DataGridCheckBoxColumn. When a user clicks on a checkbox cell, I want to determine what cells have been checked and what ones are not. Here is my code:
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
//Click event handler
private void placesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
I am finding that the DataGridCheckBoxCells are not holding the correct value at the time of the click. This occurs for all rows. There seems to be no pattern really. I was hoping that the event was just not called at the right time (I.e. the checking of the checkbox was yet to be completed) but I cannot prove that.
In short, even though the GUI displays a checked checkbox, the back end thinks the checkbox is not checked when using .Value Is there a simpler way to just determine if each cell[0] is checked or not checked in a datagridview?
I am finding that the DataGridCheckBoxCells are not holding the
correct value at the time of the click.
This is normal and by design because you are using DataGridView.CellContentClick event. From MSDN:
Use this event to detect button clicks for a DataGridViewButtonCell or
link clicks for a DataGridViewLinkCell. For clicks in a
DataGridViewCheckBoxCell, this event occurs before the check box
changes value, so if you do not want to calculate the expected value
based on the current value, you will typically handle the
DataGridView.CellValueChanged event instead. Because that event occurs
only when the user-specified value is committed, which typically
occurs when focus leaves the cell, you must also handle the
DataGridView.CurrentCellDirtyStateChanged event.
The corrected code is similar to the other answer but unfortunately, the reason of the problem was not explained in that answer.
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
Boolean bl;
if (placesGridView.Columns[e.ColumnIndex].Name == "name of check column")
{
DataGridViewCheckBoxCell checkCell = (DataGridViewCheckBoxCell)placesGridView.Rows[e.RowIndex].Cells[2]; //2 number of check column
//bl is the check box current condition.
//Change only this in your list eg list[e.RowIndex] = bl; No need to check all rows.
bl = (Boolean)checkCell.Value;
}
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
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;
}
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;
}
}