DataGrid SelectedIndex Changed Event Handler - wpf

I have a WPF DataGrid which I'm trying to auto-scroll using this code:
private void mydatagrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
mydatagrid.ScrollIntoView(mydatagrid.CurrentItem);
}
The problem I'm having is that this event handler is not called every time the SelectedIndex of the DataGrid changes. Yes, it does get called when I click on the grid or add or delete an item, but it does NOT get called upon some other actions, such as moving the currently selected item up or down in the underlying collection, which is set via:
mydatagrid.ItemsSource = Seq;
(where Seq is an object of a type derived from ObservableCollection<>).
In my troubleshooting attempts I bound the text of a TextBlock to the SelectedIndex property of mydatagrid. The index number updates perfectly, regardless of what action I perform on the datagrid and/or underlying data source.
So my question is: how can I create an event handler in C# code that gets called upon ANY and ALL changes to the value of SelectedIndex, just like my little textblock binding example does?
I've tried finding a propertyChanged event for the DataGrid, but to no avail. I've tried tapping into all of the events of the DataGrid that seemed remotely related, but to no avail. I've also tried using the CollectionChanged event of the underlying collection, but this doesn't seem to be nicely synchronized with the datagrid (at least at the moment that the event occurs).
Thanks

Related

Master-detail: How to fetch a control from a template inside the "detail" ContentControl?

I have a ListView (on the 'master' side) whose selection drives a ContentControl's Content property (on the 'detail' side). The ContentControl's visual tree comes from either of two DataTemplate resources that use DataType to choose which detail view to render based on what is selected in the ListView.
That part works fine.
The part I'm struggling with is that there is a particular control inside (one of) the templates that I need to obtain a reference to whenever it changes (e.g. the template selected changes or the ListView selection changes such that the instance of the control is recreated.)
In my ListView.SelectionChanged event handler, I find the ContentControl has not yet been updated with its new visual tree, so initially it's empty on the first selection, and for subsequent selections its visual tree matches the old selection instead of the new one.
I've tried delaying my code by scheduling on the Dispatcher with a priority as low as DispatcherPriority.Loaded, which works for the first selection but on subsequent selections my code still runs before the visual tree is updated.
Is there a better event I should be hooking to run whenever the ContentControl's visual tree is changed to reflect a changed data-bound value to its Content property?
Extra info: the reason I need to reach into the expanded DataTemplate is that I need to effectively set my view model's IList SelectedItems property to a DataGrid control's SelectedItems property. Since DataGrid.SelectedItems is not a dependency property, I have to do this manually in code.
The fix required a combination of techniques. For the first selection that populates the visual tree, I needed to handle ContentControl.OnApplyTemplate() which is only a virtual method rather than an event. I derived from it and exposed it as an event:
public class ContentControlWithEvents : ContentControl
{
public event EventHandler? TemplateApplied;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.TemplateApplied?.Invoke(this, EventArgs.Empty);
}
}
In the XAML I used the above class rather than ContentControl:
<local:ContentControlWithEvents
Content="{Binding SelectedAccount}"
x:Name="BankingSelectedAccountPresenter"
TemplateApplied="BankingSelectedAccountPresenter_TemplateApplied" />
Then I handle the event like this:
void BankingSelectedAccountPresenter_TemplateApplied(object sender, EventArgs e) => this.UpdateSelectedTransactions();
private void UpdateSelectedTransactions()
{
if (this.MyListView.SelectedItem?.GetType() is Type type)
{
DataTemplateKey key = new(type);
var accountTemplate = (DataTemplate?)this.FindResource(key);
Assumes.NotNull(accountTemplate);
if (VisualTreeHelper.GetChildrenCount(this.BankingSelectedAccountPresenter) > 0)
{
ContentPresenter? presenter = VisualTreeHelper.GetChild(this.BankingSelectedAccountPresenter, 0) as ContentPresenter;
Assumes.NotNull(presenter);
presenter.ApplyTemplate();
var transactionDataGrid = (DataGrid?)accountTemplate.FindName("TransactionDataGrid", presenter);
this.ViewModel.Document.SelectedTransactions = transactionDataGrid?.SelectedItems;
}
}
}
Note the GetChildrenCount check that avoids an exception thrown from GetChild later if there are no children yet. We'll need that for later.
The TemplateApplied event is raised only once -- when the ContentControl is first given its ContentPresenter child. We still the UpdateSelectedTransactions method to run when the ListView in the 'master' part of the view changes selection:
void BankingPanelAccountList_SelectionChanged(object sender, SelectionChangedEventArgs e) => this.UpdateSelectedTransactions();
On initial startup, SelectionChanged is raised first, and we skip this one with the GetChildrenCount check. Then TemplateApplied is raised and we use the current selection to find the right template and search for the control we need. Later when the selection changes, the first event is raised again and re-triggers our logic.
The last trick is we must call ContentPresenter.ApplyTemplate() to force the template selection to be updated before we search for the child control. Without that, this code may still run before the template is updated based on the type of item selected in the ListView.

WPF telerik GridView aggregate is not updating until focus out of a grid view field

telerik:GridView, using aggregate function to show sum in footer, the property is being updated from somewhere else and GridView is not calling PropertyChanged until focus out from GridView Column.
How we can refresh aggregate as soon as Item-source Property is changed.
I want to get aggregate updated as soon as ItemSource Property is updated.
On GridView property change I am using
private void GV_PropertyChanged(object s, PropertyChangedEventArgs e)
{
GV.CalculateAggregate();
}
According to the docs, it seems like you should raise the CollectionChanged event for the source collection:
In order to get the aggregate results refreshed, you have to notify
the GridView that the bound collection has been changed. A
CollectionChanged notification of the bound collection should be
raised.
There are four possible solutions:
You can update the value as illustrated in this help article on how to
edit an item outside RadGridView.
Ensure that a CollectionChanged event is raised from the bound source
collection.
Invoke RadGridView.CalculateAggregates() method.
Invoke RadGridView.Rebind() method. It will raise CollectionChanged
notification with action Reset. Please note that the entire view will
be recreated.

Checkbox in silverlight DataGrid behaving strangely

I am using checkbox in an itemtemplate column in a Silverlight 5 DataGrid.
I am facing a strange problem with it. When I select more than one checkbox and then scroll the grid up and down, the selection shifts to some other checkbox.
I fixed this problem in my code. I did handling within the LoadingRow and UnloadingRow events of the grid.
As soon as a row is loaded, we need to look for the condition on the basis of which we want to keep the check-box checked or unchecked. But as soon as you set the IsChecked property, Checked or UnChecked event of the check-box will get fired.
In this scenario we can unregister the Checked and UnChecked events of the check-box if we have any, set the IsChecked property. After setting this, again register the events.
Below is the code for your help.
Add LoadingRow and UnloadingRow events to your grid.
... LoadingRow="DGUserList_RowLoadUnload" UnloadingRow="DGUserList_RowLoadUnload">
In your code behind file:
private void DGUserList_RowLoadUnload(object sender, DataGridRowEventArgs e)
{
DataGridRow row = e.Row;
CheckBox cbox = (CheckBox)this.dgUserList.Columns[0].GetCellContent(row);
this.UpdateHookedEventsForCheckBox(cbox, false);
cbox.IsChecked = true; // Here put your condition for check/uncheck
this.UpdateHookedEventsForCheckBox(cbox, true);
}
private void UpdateHookedEventsForCheckBox(CheckBox chkBox, bool register)
{
if (register)
{
chkBox.Checked += this.CheckBox_Checked;
chkBox.Unchecked += this.CheckBox_Unchecked;
}
else
{
chkBox.Checked -= this.CheckBox_Checked;
chkBox.Unchecked -= this.CheckBox_Unchecked;
}
}
This way I need not bother about putting some hake code in my Checked and UnChecked events.
This is a known behaviour since Silverlight is re-using its graphical resources in the DataGrid. There's a discussion about it in this Silverlight thread.
It seems one way to fix it is to databind the IsSelected property:
My solution at that time was to add a new property in my data source:
IsSelected, and to bind the checkbox to that value.
You have more additional info in this thread, where Microsoft answers:
This is not a bug. What happens when you scroll around in the
DataGrid is the same checkboxes are being used for new data because
the DataGrid recycles the visuals. When your new data has different
values, the check will change through the Binding and you'll receive
the event. What you can do to get this scenario to work is to listen
to LoadingRow which is raised when a row comes into view. In there,
you can call column.GetCellContents to get the contents of the cell.
This will give you the CheckBox, and you can attach to CheckChanged at
this time. If you do this, you need to do something similar and
listen to UnloadingRow so you can detach the eventhandler when the
checkbox is scrolled out of view.

Silverlight Datagrid Refresh Data with SelectionChanged Binding

I am building an issue tracking system that uses Silverlight. I use DataGrids to display the issue lists, set the selected index to -1 so that no row appears selected and then use the selection change event to popup an issue details window for the particular selected issue.
When I try to refresh the DataGrid by rebinding it to its ItemsSource, I disable the SelectionChanged event, rebind the DataGrid to its ItemsSource, set the SelectedIndex to -1 and then enable the SelectionChanged event again. However, no matter how late I leave the re-enabling of the SelectionChanged event (even until after the DataGrid_Loaded event), a SelectionChanged event is fired and the issue details window pops up.
Is there a better way to refresh the data in a DataGrid that won't cause the SelectedIndex to change? If not, is there a way of telling which events are caused by a programmatic index change and not a human interaction?
(Also up for discussion, is this the best control for the job? I need to display multiple fields per row, such as the issue title, assigned user, requested by user, status, etc.)
Thanks in advance.
I have had a similar issue in the past with the comctl32 ListView control's selection events: Programmatic selection cause selection change events to be raised.
My workaround for this issue is to have a per-grid/list counter variable that lets the event handler know if it should care about the selection event or not. The code would go something like:
int issueList_ProgrammaticEventCount_Selection = 0;
void refreshIssueList()
{
++issueList_ProgrammaticEventCount_Selection;
issueList.ItemsSource = ...;
}
void issueList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (issueList_ProgrammaticEventCount_Selection > 0)
{
--issueList_ProgrammaticEventCount_Selection;
return;
}
showIssueDetails();
}

WPF DataGrid how to get when ItemsSource updates

Which event fires when DataGrid's source is updating? I've tried DataContextChanged and SourceUpdated but it never worked out.
Actually I need a simple thing. I want, if there is a new row comes, scroll the GridView's scrollbar down to the bottom to see what it was.
I had the same problem and I manage it this way
DataGrid myGrid = new DataGrid();
CollectionView myCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(myGrid.Items);
((INotifyCollectionChanged)myCollectionView).CollectionChanged += new NotifyCollectionChangedEventHandler(DataGrid_CollectionChanged);
You then need to implement the logic in the event handler DataGrid_CollectionChanged.
Set NotifyOnTargetUpdated = true for the ItemsSource binding and handle TargetUpdated event. If you've multiple bindings, then look for DataTransferEventArgs Property to find out if the target is ItemsSource or not.
If you are trying to have the grid refresh when something is added to the database itself, that's not going to happen. I'm more familiar with WinForms than WPF but I'm assuming there is no magical way to keep a grid in sync with the database without writing some background process that continuously checks for database changes.
If you are updating the actual data source of the grid (ex. Collection) then that will update the grid.
For my part i've used SelectionChange notification which raise each event Del/Add/Edit/Select
It's work very well
private void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine("hi");
}

Resources