I have a datagrid that I fill with data from a database.
When I click on a row, I call the GotFocus method and try to make a button visible if certain requirements are met.
private void dtgVerkoopsdocumenten_GotFocus(object sender, RoutedEventArgs e)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
if (row.soort2 == "Factuur")
{
btnBoeking.IsHitTestVisible = true;
btnBoeking.Opacity = 1;
}
else
{
btnBoeking.IsHitTestVisible = false;
btnBoeking.Opacity = 0.5;
}
}
This gives me an error.
Index was out of range. Must be non-negative and less than the size of the collection.
Now when I call the code but from a button click it does it how it's supposed to work.
private void tester_Click(object sender, RoutedEventArgs e)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
test.Content = row.soort2;
if (row.soort2 == "Factuur")
{
btnBoeking.IsHitTestVisible = true;
btnBoeking.Opacity = 1;
}
else
{
btnBoeking.IsHitTestVisible = false;
btnBoeking.Opacity = 0.5;
}
}
Why is this?
Why dont you use DataGrid SelectedIndexChanged event?
Wyy use GotFocus that doesnt tell you if user even made a selection to start with,
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
Called from gotfocus will fail as you have nothing selected besides having no error check in place to check if selection,
If you use Selection changed events you know the user has made selection changes there are a number of events for selection
before access selected items by index you need to check selected item count is grater than zero condition.
Because dtgVerkoopsdocumenten.SelectedItems are empty and GotFocus event raise before SelectedItemChanged event so we are not sure the dtgVerkoopsdocumenten.SelectedItems have any item or not.
You can check dtgVerkoopsdocumenten.SelectedItems before do anything.
if (dtgVerkoopsdocumenten.SelectedItems != null &&
dtgVerkoopsdocumenten.SelectedItems.Count > 0)
{
DataItem row = (DataItem)dtgVerkoopsdocumenten.SelectedItems[0];
...
}
Related
I have a View Model in which my data is stored to be used on the presenter and window. However, I now need to get (when pressing a button) the current row's results on the grid into my view model. I first used the mouse double click event, but the requirements changed from that to a button on the form:
private void dgvResults_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (this.colOrderNo.Index != e.ColumnIndex || e.RowIndex < 0) return;
var auditOrder = (PurchaseOrdersTrackingViewModel) this.dgvResults.Rows[e.RowIndex].DataBoundItem;
this.AuditOrderKey = $"{auditOrder.OrderNo}|{auditOrder.ItemNo}|{auditOrder.WarehouseId}|{auditOrder.ItemSequence}";
}
That's my old code. Now I need that here:
private void btnAuditLog_Click(object sender, EventArgs e)
Not sure how to refer to the RowIndex without the DataGridViewCellMouseEventArges event. I tried the CurrentRow property, but it doesn't do the job.
Just another question on the fly, if someone could assist with that as well, how do I get a value from one form to another, spesifically a properties value. It's not on the grid, in a control. It's basically just public string order that's all. I set the value on one form, then need it for query filtering on another. Let me know if you need anything else.
You want to use the SelectedRows property of the DataGridView. It tells which row is selected. In this case, you seem to want the first selected row (since you have a single property called this.AuditOrderKey).
The column order no to longer matters in this context.
if (dgvResults.SelectedRows.Count == 0) return;
var auditOrder = (PurchaseOrdersTrackingViewModel) dgvResults.SelectedRows[0].DataBoundItem;
this.AuditOrderKey = $"{auditOrder.OrderNo}|{auditOrder.ItemNo}|{auditOrder.WarehouseId}|{auditOrder.ItemSequence}";
If you don't have selected rows, CurrentRow is an option:
if (dgvResults.CurrentRow == null) return;
var auditOrder = (PurchaseOrdersTrackingViewModel) dgvResults.CurrentRow.DataBoundItem;
this.AuditOrderKey = $"{auditOrder.OrderNo}|{auditOrder.ItemNo}|{auditOrder.WarehouseId}|{auditOrder.ItemSequence}";
Use the SelectedRows property of the DataGridView. Assuming that you set your DataGridView as MultiSelect = false; and SelectionMode = FullRowSelect;
if (dgvResults.SelectedRows.Count > 0)
{
dgvResults.SelectedRows[0].Cells["yourColumnName"].Value.ToString();
}
In your button click event, it will look like this. (Assuming that your button name is btnEdit)
private void btnEdit_Click(object sender, EventArgs e)
{
if (dgvResults.SelectedRows.Count > 0)
{
string selectedValue = dgvResults.SelectedRows[0].Cells["yourColumnName"].Value.ToString();
}
}
I have a DataGrid that has RowDetailsVisibilityMode set to "VisibleWhenSelected" so that the RowDetailsTemplate displays when I select a particular row. What I want is to be able to toggle the RowDetails visibility when I click the row again. By default, the RowDetails will be visible so long as its selected, but I want to essentially "unselect" the selected row if I click it again.
After trying a lot of things, I've found a sort of wacky solution that at least makes it so that I can toggle the visibility when I click the row:
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex) { //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = -1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
}
The delay makes sure that the SelectedIndex property is already updated when I check for the selected index. I wish I didn't have to use it - I would be happy to find a better solution.
The problem now, however, is that whenever I click anything in the RowDetails of the selected row, this event fires and the row details disappear again. I want to be able to somehow check that I only expand/collapse the row when I click the row itself, not when I click whatever's in the RowDetailsTemplate. Is that possible? I don't want to use an extra Expander button for each row.
I found a solution to my problem:
In my RowDetailsTemplate under the ContentControl, I added an EventSetter that captured the PreviewMouseDown event:
<EventSetter Event="PreviewMouseDown" Handler="ContentControl_PreviewMouseDown" />
The Handler for the event set a global variable that indicated whether I had clicked on what's in the ContentControl:
private void ContentControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickedRowDetails = true;
}
Finally, in my original code, I simply checked whether I had clicked on the ContentControl, or whether it was something else, which in this case, meant it was the row itself:
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickedRowDetails = false; //default assume that we haven't clicked row details
await Task.Delay(1); //wait for other events to fire
if (!ClickedRowDetails) {
if (DataGrid.SelectedIndex == prevSelectedIndex )
{
DataGrid.SelectedIndex = -1;
}
prevSelectedIndex = DataGrid.SelectedIndex;
}
}
So now, I can successfully expand/collapse rows by simply clicking on them, and then I can manipulate whatever's in the RowDetailsTemplate without accidently collapsing the row. This was done without an Expander button.
I think a global boolean variable can do the toggle for you.
public bool Visible = false;
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (Visible == false)
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex)
{ //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = 1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
Visible = true;
}
else
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex)
{ //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = -1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
Visible = false;
}
}
In the UI I'm working with, in a DataGrid, there are cases where the user might select a different row, but after a dialog interaction, the old row needs to be shown as being selected once again. If I simply try
BundleQueueDG.SelectedIndex = currentBundleIndex;
that does not do anything and in fact, once the SelectionChanged method exits, it changes to the new value. What is the best way to "re-select" the previously-selected row?
Try setting the SelectedItem property. Preserve what was selected before and set the SelectedItem with what was previously selected in your event.
Something like this:
private void DgDataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
int newIndex = (sender as DataGrid).SelectedIndex / 2;
if (Convert.ToInt32(newIndex) >= 1)
(sender as DataGrid).SelectedItem = previous;
else
{
previous = (sender as DataGrid).CurrentItem;
}
}
I've a custom combobox template because of some binding stuff that won't work with the 'default' ComboBoxColumn.
To make it look 'nice' I've one template for the edit mode (a Combobox) and one for the 'normal' mode (a Label).
Now, because of that I've to commit the edit made to the combobox manually inside the CellEditEnding event
private bool changeCommitInProgress = false;
private void table_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.EditingElement is ContentPresenter && e.EditAction == DataGridEditAction.Commit)
{
if (!changeCommitInProgress)
{
changeCommitInProgress = true;
DataGrid grid = (DataGrid)sender;
grid.CommitEdit(DataGridEditingUnit.Row, false);
changeCommitInProgress = false;
}
}
}
The problem with this is, that it'll remove the focus from the entire datagrid. Just to be on the safe side, these are the only properties I changed on the datagrid (aside from the Name property and the ItemsSource):
grid.AutoGenerateColumns = false;
grid.IsSynchronizedWithCurrentItem = true;
grid.SelectionUnit = DataGridSelectionUnit.Cell;
This is a fun question. I have done similar with a nested DataList where I had to add a new row after last entry and focus on the first textbox of the newly generated row, maybe you can extrapolate my strategy to fit your situation?
protected void calcAvg(object sender, CommandEventArgs e)
{
int row = Convert.ToInt32(e.CommandArgument.ToString()) - 1;
DataListItem ActiveRow = dlMeasurements.Items[row];
// Snipped code doing stuff with current row
// Compare how many rows completed to number of rows requested
if (!(row + 1 == Convert.ToInt32(txtSample.Text)))
{
// Create new row
DataRow drNew = nextMeas.Tables[0].NewRow();
nextMeas.Tables[0].Rows.Add(drNew);
// Change item index and rebind
dlMeasurements.EditItemIndex = row + 1;
dlMeasurements.DataSource = nextMeas.Tables[0];
dlMeasurements.DataBind();
//Set focus with the Script Manager
smInspection.SetFocus((TextBox)(dlMeasurements.Items[row + 1].FindControl("txtRead1")));
}
else
{
// Otherwise close the measurements and show exit button
dlMeasurements.EditItemIndex = -1;
dlMeasurements.DataSource = nextMeas.Tables[0];
dlMeasurements.DataBind();
btnSaveAndPrint.Visible = true;
}
}
}
I've got a ListBox with a bunch of items in it. The user can click an item to edit its contents. How do I prevent the user from deselecting all items? i.e., the user shouldn't be able to have nothing selected.
There is a case missing in your situation, which is when the list is cleared you will reselect an item there is no longer on the list. I solve this by adding an extra check.
var listbox = ((ListBox)sender);
if (listbox.SelectedItem == null)
{
if (e.RemovedItems.Count > 0)
{
object itemToReselect = e.RemovedItems[0];
if (listbox.Items.Contains(itemToReselect))
{
listbox.SelectedItem = itemToReselect;
}
}
}
I then put this inside a behaviour.
I'm not sure if there is a direct way to disable deselecting an Item, but one way which would be transparent to the user is to keep track of the last selected Item, and whenever the SelectionChanged event is raised and the selected index is -1, then reselect the last value.
This Works for Sure to Prevent User from Deselect... Add those 2 Events to your checkedListBox1 and set the Property CheckOnClick to "True" in Design Mode. (MSVS2015)
private void checkedListBox1_SelectedValueChanged(object sender, EventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
private void checkedListBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
To disable on or more options in your listbox/dropdown, you can add the "disabled" attribute as shown below. This prevent the user from selection this option, and it gets a gray overlay.
ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);
One solution, as suggested by amccormack:
private void hostsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(lstHosts.SelectedItem == null)
{
if(e.RemovedItems.Count > 0)
{
lstHosts.SelectedItem = e.RemovedItems[0];
}