Is there an event somewhere in the Silverlight control model that is raised once an item is databound? I am binding at design time to a large amount of data and would like to display an animation until the databinding is complete.
There is no specific event that is fired when databinding is completed. Your best bet would probably be to key off of the FrameworkElement.LayoutUpdated event. This is the last event in the lifecycle before a control is ready for user interaction. However, this event will continue to be raised many more times due to property changes, size changes, and explicit calls to UpdateLayout() or InvalidateArrange(). Therefore you will have to add some extra logic to make sure that the LayoutUpdated event warrants stopping/hiding your animation, such as only doing it the first time or if you are sure the event was fired due to a change in databinding.
If the control is actually your own custom control and you are binding to custom DependencyProperties on that control then you could raise your own event on the PropertyChangedCallbacks for each of the properties to signal that they have been updated via databinding.
Here's what I do:
private object lastDataContext;
private void MyClass_Loaded(object sender, RoutedEventArgs e)
{
if (DataContext != lastDataContext)
{
perform_onetime_operation();
lastDataContext = DataContext;
}
}
That way perform_onetime_operation will get called not just the first time databinding happens, but any time that the DataContext changes meaning that data is re-bound.
Related
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
I'm currently encountering this issue while programming Metro, but I'm sure it applies to WPF, Silverlight and possibly even WinForms.
I have a databound TextBox and a method attached to the LostFocus event of the TextBox. When it loses focus, I want the code behind to trigger a save function in order to persist the data. The problem is that the LostFocus-event triggers before the business object is updated from the GUI through TwoWay databinding.
What is the best way to handle this? Is there some way to force update of the databinding from the LostFocus method (would probably be platform-specific)?
It sounds like you're wanting to do some business logic instead of having the GUI update the textbox. I would set the Textbox to only be BindingMode.OneWay, so that when the business object is changed the textbox is updated properly.
Then in the Lostfocus event (again wpf, but I think you'll get the idea):
private void EditBoxLostFocus( object sender, KeyboardFocusChangedEventArgs e )
{
var textbox = sender as TextBox;
//update your business object w/ textbox.Text value
//other business logic
}
This way you're in full control of when the object gets updated, and can apply your business rules.
Set UpdateSourceTrigger = PropertyChanged on the TextBox binding.
http://msdn.microsoft.com/en-us/library/system.windows.data.updatesourcetrigger(v=vs.100).aspx
When I set the .ItemSource() property on a DataGrid to a Collection, the call returns fast, but the actual binding happens afterwards. Since I want to display a waiting cursor, I need to detect when the actual binding has finished. Is there any event for this?
Anything based on ItemsControl uses an ItemContainerGenerator to generate its items in the background. You can access the ItemContainerGenerator property of the DataGrid and hook up the StatusChanged event to determine when it's done. If you're using virtualization and scroll, this will fire again so you need to handle that if necessary in your case.
I waited for my DataGrid's Loaded event to fire, and I did a BeginInvoke, like this:
private void SubjectsList_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => ColorMyRows()));
}
More details available in my answer here: https://stackoverflow.com/a/44464630/2101117
Your best bet is to hook into OnPropertyChanged event in your Window or User Control. This event is fired every time a property is updated. Then check for the actual property you wish to observe and take action.
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if ("YOUR_PROPERTY_NAME".Equals(e.Property.ToString()))
{
// Take action
}
base.OnPropertyChanged(e);
}
I want to get notified when an item in a ListBox gets clicked by the mouse, whether it is already selected or not.
I searched and found this: (http://kevin-berridge.blogspot.com/2008/06/wpf-listboxitem-double-click.html see the comments)
private void AddDoubleClickEventStyle(ListBox listBox, MouseButtonEventHandler mouseButtonEventHandler)
{
if (listBox.ItemContainerStyle == null)
listBox.ItemContainerStyle = new Style(typeof(ListBoxItem));
listBox.ItemContainerStyle.Setters.Add(new EventSetter()
{
Event = MouseDoubleClickEvent,
Handler = mouseButtonEventHandler
});
}
//Usage:
AddDoubleClickEventStyle(listView1, new MouseButtonEventHandler(listView1_MouseDoubleClick));
This works, but it does it for a DoubleClick. I can't get it working for a single click though. I tried MouseLeftButtonDownEvent - as there doesn't seem to be a MouseClick event, but it's not being called.
A bit more general side question: How can I see what events do exist and which handlers correspond to them and when they actually do something? For example, what tells me that for a MouseDoubleClickEvent I need a MouseButtonEventHandler? Maybe for a MouseLeftButtonDownEvent I need some other handler and that's why it's not working?
I also tried subclassing ListBoxItem and override OnMouseLeftButtonDown - but it doesn't get called either.
Marc
I believe that your MouseLeftButtonDown handler is not called because the ListBox uses this event internally to fire its SelectionChanged event (with the thought being that in the vast majority of cases, SelectionChanged is all you need). That said, you have a couple of options.
First, you could subscribe to the PreviewLeftButtonDown event instead. Most routed events have a routing strategy of Bubbling, which means that the control that generated the event gets it first, and if not handled, the event works its way up the visual tree giving each control a chance at handling the event. The Preview events, on the other hand, are Tunneling. This means that they start at the root of the visual tree (generally Window), and work their way down to the control that generated the event. Since your code would get the chance to handle the event prior to the ListBoxItem, this will get fired (and not be handled) so your event handler will be called. You can implement this option by replacing MouseDoubleClickEvent in your sample with PreviewMouseLeftButtonDown.
The other option is to register a class handler that will be notified whenever a ListBoxItem fires the MouseLeftButtonDown event. That is done like this:
EventManager.RegisterClassHandler(typeof(ListBoxItem),
ListBoxItem.MouseLeftButtonDownEvent,
new RoutedEventHandler(this.MouseLeftButtonDownClassHandler));
private void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
{
}
Class Handlers are called before any other event handlers, but they're called for all controls of the specified type in your entire application. So if you have two ListBoxes, then whenever any ListBoxItem is clicked in either of them, this event handler will be called.
As for your second question, the best way to know what type of event handler you need for a given event, and to see the list of events available to a given control, is to use the MSDN documentation. For example, the list of all events handled by ListBoxItem is at http://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem_events.aspx. If you click on the link for an event, it includes the type of the event handler for that event.
There is also another way - to handle PreviewMouseDown event and check if it was triggered by the list item:
In XAML:
<ListBox PreviewMouseDown="PlaceholdersListBox_OnPreviewMouseDown"/>
In codebehind:
private void PlaceholdersListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var item = ItemsControl.ContainerFromElement(sender as ListBox, e.OriginalSource as DependencyObject) as ListBoxItem;
if (item != null)
{
// ListBox item clicked - do some cool things here
}
}
Was inspired by this answer, but it uses listbox by name, I propose to use sender argument to avoid unnecessary dependencies.
I think the first option in Andy's answer, of using PreviewMouseLeftButtonDown, is the way to go about this. In XAML it would look like this:
<ListBox Name="testListBox">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter
Event="PreviewMouseLeftButtonDown"
Handler="ListBox_MouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
There is another way to get MouseDown event in ListBox. You can add event handler for events that are marked as handled by using handledEventsToo signature of AddHandler method:
myListBox.AddHandler(UIElement.MouseDownEvent,
new MouseButtonEventHandler(ListBox_MouseDown), true);
Third parameter above is handledEventsToo which ensures that this handler will be invoked no matter if it is already marked as Handled (which ListBoxItem does in ListBox).
See Marking Routed Events as Handled, and Class Handling for explanation.
See How to Attach to MouseDown Event on ListBox for example.
You can use Event="MouseLeftButtonUp"
Unlike "PreviewLeftButtonDown" it will get the ListBoxItem handled too.
You can use the SelectionChangedEventArgs argument of the SelectionChanged event to find what item is add or removed through AddedItems and RemovedItems, usually only have the latest clicked on, or if not, then look at the last item which is the count-1.
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");
}