Wpf event when DataContext is modified - c#

You know how a TextBox will have its content modified when the DataContext changes (not replaced, just modified)? Well I want to be notified just like the UI is, with some sort of event on the DataContext. So far, I haven't found a way to do this, and I'm about to give up and simply subscribe to all the events on each INotifyPropertyChanged in my DataContext. I know there's the DataContextChanged / DataContextChanging in the Window class, but so far I either can't get it to work, or this only fires when the DataContext is replaced completely. Is there a way to do this?

Think about what is being asked. If any property changes on a data context a general event is to fire.
What process is available which can do that operation externally?
Decentralized Solution
Properties do not provide a change notification unless they are manually programmed to do so themselves; hence why INotifyPropertyChange is the route to normally use.
Centralized Solution
Otherwise a separate manager will need to reflect off of the instance taking a snapshot of all the properties. Then on a timer the manager will poll the instance for a current snapshot and compare that one to the old snapshot. If a change is detected an event can be fired and the new snapshot replaces the old.

DataContextChanged event is only fired when the DataContext of the Window has changed completely (set to null, or a new instance, etc.). I believe you are down the correct path, and inside your ViewModel, you will need to subscribe to NotifyPropertyChanged. In the event handler, you can switch on the corresponding property like so:
private void OnNotifyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.Property)
{
case "Property1":
break;
case "Property2":
break;
case "Property3":
break;
}
}

Related

When to Call Method after Property Change

I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.

RejectChanges() with ViewModel and MVVM design pattern and UI update

I am having a problem with DomainContext.RejectChanges() and reflecting the rollback in the UI. Here is my scenario.
I have a Model (Entity) generated for use with RIA services (I'll call it Foo)
I have a ViewModel that wraps Foo and extends it (I'll call it FooViewModel)
I have a View that is using Binding to display and update data using the FooViewModel
I have an "outer" ViewModel that holds an ObservableCollection of FooViewModels
The "outer" View has a list box bound to the ObservableCollection
So essentially there is a listbox of FooViewModels on one screen...when you select an item a childwindow is displayed to edit that particular FooViewModel. The FooViewModel is serving both the listbox and the childwindow.
Editing works just fine. A change in the childwindow reflects in the listbox immediately because I am calling RaisePropertyChanged() when the viewmodel properties are updated.
However, If I perform a DomainContext.RejectChanges()...the underlying entity gets rolled back (all changes reverted as expected)...however the FooViewModel isn't aware that this change has occurred and thus the UI isn't updated. If I reselect the item in the listbox on the first screen, the childwindow is displayed with the rolled back changes (which is what I want). The listbox still isn't updated though.
When I reject changes, if I kludge a RaiseProperyChanged() for the field that I changed...the UI listbox does update.
How do I get the UI to update when the underlying entity is rejected?? And how do I do it without tracking what properties of the viewmodel were rolledback? There has to be an easy way to accomplish this that I am just missing.
Something you could try is use the PropertyChanged event on the underlying entity Foo to trigger a RaisePropertyChanged pass on the FooViewModel properties.
so making some assumptions (so this code make sense):
You have a private variables in your FooViewModel
private Foo _foo;
private DomainContext _context;
You have a method on your FooViewModel that is calling RejectChanges() on your domain context.
Like so:
public void RejectChanges()
{
_context.RejectChanges();
}
We have a method that raises the PropertyChanged event on our FooViewModel
Like so:
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
Ok, now we have that established, lets have a look at exactly what happens when you call RejectChanges() on a domain context.
When you call RejectChanges() this bubbles down through the DomainContext to its EntityContainer, then to each EntitySet in that container and then to each Entity in the set.
Once there (and in the EntitySet), it reapplies the original values if there was any, removes the entity if it was added, or adds it if it was deleted. If there was changes to the values, then it applies them back to the properties.
So theoretically, all the RaisePropertyChanged(), that are generated in the entity properties, should be triggered.
NOTE: I haven't actually tested this. If this isn't the case, then none of this works :P
So we can hook into PropertyChanged event of the Foo entity, and raise the PropertyChanged event on our FooViewModel.
so our RejectChanges() method might look like this:
public void RejectChanges()
{
Func<object, PropertyChangedEventArgs> handler = (sender, e) =>
{
RaisePropertyChanged(e.PropertyName);
};
_foo.PropertyChanged += handler;
_context.RejectChanges();
_foo.PropertyChanged -= handler;
}
So we hook up an event handler to our Foo entity, which calls the FooViewModel.RaisePropertyChanged method with the property name that is changing on the Foo entity.
Then we reject changes (which triggers the property changes),
then we unhook the event handler.
Pretty long winded, but I hope this helps :)
I presume that the call to DomainContext.RejectChanges() is happening within the ViewModel as you probably bound that to some command or method called from the parent ViewModel. Since all your bindings to the data is done on the ViewModel properties you will have to raise the property change event on them when you directly manipulate the model outside of those properties. Which you probably doing already.
public void RejectChanges()
{
DomainContext.RejectChanges();
RaisePropertyChangeOnAll();
}
How you implement RaisePropertyChangeOnAll() can be done simply with a list of RaisePropertyChange("...") for each property or you could do it through reflection (if Silverlight permissions allow, not too sure about it) by adding an Attribute on each property you want to raise. Find all the properties that are tagged with it and call RaisePropertyChanged on the MemberInfo.Name value.
[Raiseable]
public string SomeValue
{
...
}
Just an idea but may not be a perfect solution.

Refresh DataContext for Views in WPF?

I am working with a WPF application and using my own architecture that strongly resembles a M-V-VM /MVC. I have controllers for each of the views, and I have ViewModels that are bound to the Views.
For Example, I have a ToolBarView that has a corresponding ToolBarViewModel, and ToolBar Controller.
I am using notifications to update all the views so that they do not need to reference each other, but that is not relevant for my question.
Each of the Views is listening for an event to trigger in their controller when the model has been updated. In the ToolBarView this looks like the following.
/*In Constructor in ToolbarView*/
Controller.Updated += UpdateView
/*Event Handler in ToolbarView*/
private void Updateview(object sender,EventArgs e)
{
DataContext = Controller.Model;
//Other Updating if needed
}
If not obvious, what the above code is doing is saying that when the Controller fires the Updated event, to invoke the UpdateView(object sender,EventArgs e).
The problem that I am experiencing is that the first time that the UpdateView() is invoked, everything is working fine, but when it is invoked the second time, the DataContext seems to still be bound to the original Controller.Model.
It seems almost as if I have to release the DataContext, or refresh it in order for it to be bound to the Model every time.
The Controller is performing operations on the Model, and therefor when the UpdateView() is invoked, it needs to display the newly assigned values on that model.
Is there some way I need to refresh the DataContext, or is there a different way I need to do this?
If you are assigning the DataContext to the same instance of your model, the in effect it won't "change". WPF expects objects to notify it when their state changes, either through DependencyProperty properties or by implementing INotifyPropertyChanged.
So if you do something like:
MyObject o = new MyObject();
o.MyString = "One";
this.DataContext = o;
// ... some time later ...
o.MyString = "Two";
this.DataContext = o;
Assuming MyObject doesn't implement INotifyPropertyChanged, then the second assignment to DataContext is effectively worthless. You would have to set DataContext to null, then assign your object again to have it "refresh".
But your best bet in general would be to implement INotifyPropertyChanged. This would end up being much more efficient, as only the property that actually change would need to be updated in the UI.

How not to lose binding source updates?

Suppose I have a modal dialog with a textbox and OK/Cancel buttons. And it is built on MVVM - i.e. it has a ViewModel object with a string property that the textbox is bound to.
Say, I enter some text in the textbox and then grab my mouse and click "OK". Everything works fine: at the moment of click, the textbox loses focus, which causes the binding engine to update the ViewModel's property. I get my data, everybody's happy.
Now suppose I don't use my mouse. Instead, I just hit Enter on the keyboard. This also causes the "OK" button to "click", since it is marked as IsDefault="True". But guess what? The textbox doesn not lose focus in this case, and therefore, the binding engine remains innocently ignorant, and I don't get my data. Dang!
Another variation of the same scenario: suppose I have a data entry form right in the main window, enter some data into it, and then hit Ctrl+S for "Save". Guess what? My latest entry doesn't get saved!
This may be somewhat remedied by using UpdateSourceTrigger=PropertyChanged, but that is not always possible.
One obvious case would be the use of StringFormat with binding - the text keeps jumping back into "formatted" state as I'm trying to enter it.
And another case, which I have encountered myself, is when I have some time-consuming processing in the viewmodel's property setter, and I only want to perform it when the user is "done" entering text.
This seems like an eternal problem: I remember trying to solve it systematically from ages ago, ever since I've started working with interactive interfaces, but I've never quite succeeded. In the past, I always ended up using some sort of hacks - like, say, adding an "EnsureDataSaved" method to every "presenter" (as in "MVP") and calling it at "critical" points, or something like that...
But with all the cool technologies, as well as empty hype, of WPF, I expected they'd come up with some good solution.
At critical points, you can force the binding to push through to your view model:
var textBox = Keyboard.FocusedElement as TextBox;
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
Edit:
OK, since you don't want hacks we have to face the ugly truth:
In order to implement a clean view, the properties exposed by your view model should be friendly to frequent binding updates.
An analogy we can use is a text editor. If the application was a giant text box bound to a file on disk, every keystroke would result in writing the whole file. Even the concept of saving is not needed. That's perversely correct but terribly inefficient. We all immediately see that the view model needs to expose a buffer for the view to bind to and this re-introduces the concept of save and forces state handling in our view model.
Yet, we see this is still not efficient enough. For even medium-sized files the overhead of updating the whole-file buffer on every keystroke becomes unbearable. Next we expose commands in our view model to efficiently manipulate the buffer, never actually exchanging the whole buffer with the view.
So we conclude that in order to achieve efficiency and responsiveness with pure MVVM, we need to expose an efficient view model. That means that all text boxes can be bound through to properties with no ill effects. But, it also means that you have to push state down into the view model to handle that. And that's OK because a view model is not the model; it's job is it to handle the needs of the view.
It's true that we can rapidly prototype user interfaces by taking advantage of shortcuts like binding on focus changes. But binding on focus changes can have negative consequences in real applications and if so then we should simply not use it.
What is the alternative? Expose a property friendly to frequent updates. Call it the same thing as the old inefficient property was called. Implement your fast property using the slow property with logic that depends on the state of the view model. The view model gets the save command. It knows whether the fast property has been pushed through to the slow property. It can decide if when and where the slow property will be synched to the model.
But you say, haven't we just moved the hack from the view to the view model? No, we have lost some elegance and simplicity, but go back to the text editor analogy. We have to solve the problem, and it is the view model's job to solve it.
If we want to use pure MVVM and we want efficiency and responsiveness, then lame heuristics like let's avoid updating the binding source until the element loses focus won't help. They introduce as many problems as they solve. In that case, we should let the view model do its job, even if means adding complexity.
Assuming we accept it, how can we manage the complexity? We can implement a generic wrapper utility class to buffer the slow property and allow the view model to hook its get and set methods. Our utility class can auto-register for save command events to reduce the amount of boilerplate code in our view model.
If we do it right, then all the parts of the view model that were fast enough to be used with property changed binding will all still be the same, and the others that were worthy of asking the question "Is this property too slow?" will have a small amount of code to address the issue, and the view is none the wiser.
This is a tricky one and I agree a non-hack and more-or-less code free solution should be found. Here are my thoughts:
The view is responsible because it sets IsDefault to true and allows for this 'problem'
The ViewModel should not be responsible in any way to fix this it might introduce dependencies from VM to V and thus breaking the pattern.
Without adding (C#) code to the View all you can do is either change bindings (e.g. to UpdateSourceTrigger=PropertyChanged) or add code to a base class of the Button. In the base class of the button you might be able to shift focus to the button before executing the command. Still hack-ish but cleaner than adding code to the VM.
So at the moment the only 'nice' solutions I see require the view developers to stick to a rule; set the binding in a specific way or use a special button.
I would add a Click event handler for the default button. The button's event handler is executed prior the command will be called, so the data bindings can be updated by changing the focus in the event handler.
private void Button_Click(object sender, RoutedEventArgs e) {
((Control)sender).Focus();
}
However, I don't know if similar approach can be used with other shorcut keys.
Yes, I have quite some experience. WPF and Silverlight still have their pain areas. MVVM doesn't solve it all; it is not a magic bullet and the support in the frameworks is getting better but still lacks. E.g., I still find editing deep child collections a problem.
At the moment I handle these situations case by case because a lot depends on the way the individual view have to work. This is what I spend most of my time on because I generate a lot of plumbing using T4 so I have time left for these quirks.
The problem is that the TextBox's text has a default source trigger of LostFocus instead of PropertyChanged. IMHO this was a wrong default choice since it is quite unexpected and can cause all sorts of problems (such as the ones you describe).
The simplest solution would be to always explicitly use UpdateSourceTrigger=PropertyChanged (as the others suggested).
If this isn't feasible (for whatever reason), I would handle the Unloaded, Closing or Closed events and manually update the binding (as shown by Rick).
Unfortunately it seems that certain scenarios are still a bit problematic with a TextBox, so some workarounds are necessary. For example, see my question. You might want to open a Connect bug (or two) with your specific problems.
EDIT:
Pressing Ctrl+S with focus on the TextBox, I would say the behavior is correct. After all, you are executing a command. This has nothing to do with the current (keyboard) focus. The command may even depend on the focused element! You are not clicking on a button or similar, which would cause the focus to change (however, depending on the button, it may fire the same command as before).
So if you want to only update the bound Text when you lose focus from the TextBox, but at the same time you want to fire a command with the newest contents of TextBox (i.e. the changes without it having lost focus), this does not match up. So either you have to change your binding to PropertyChanged, or manually update the binding.
EDIT #2:
As for your two cases why you cannot always use PropertyChanged:
What precisely are you doing with StringFormat? In all my UI work so far I use StringFormat to reformat data I am getting from the ViewModel. However, I am not sure how using StringFormat with data which is then again edited by the user should work. I am guessing you want to format the text for display, and then "unformat" the text the user enters for further processing in your ViewModel. From your description, it seems it isn't "unformatted" correctly all the time.
Open a Connect bug where it isn't working as it should.
Write your own ValueConverter which you use in the binding.
Have a separate property with the last "valid" value and use that value in your ViewModel; only update it once you get another "valid" value from the property you use in databinding.
If you have a long-running property setter (ex. a "validate" step), I would do the long-running part in a separate method (getters and setters should normally be relatively "fast"). Then run that method in a worker thread/threadpool/BackgroundWorker (make it abortable so you can restart it with a new value once the user enters more data) or similar.
What you think about proxy command and KeyBinding to ENTER key?
EDITED:
There we have one utility command (like converter), which requires knowledge about concrete view. This command can be reused for any dialog with same bug. And you add this functionality/hack only in view where this bug exists, and VM will be clear.
VM creates to adapt business to view and must provide some specific functionality like data conversion, UI commands, additional/helper fields, notifications and hacks/workarounds. And if we have leaks between levels in MVVM we have problems with: high connectivity, code reuse, unit testing for VM, pain code.
Usage in xaml (no IsDefault on Button):
<Window.Resources>
<model:ButtonProxyCommand x:Key="proxyCommand"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding Source={StaticResource proxyCommand}, Path=Instance}"
CommandParameter="{Binding ElementName=_okBtn}"/>
</Window.InputBindings>
<StackPanel>
<TextBox>
<TextBox.Text>
<Binding Path="Text"></Binding>
</TextBox.Text>
</TextBox>
<Button Name="_okBtn" Command="{Binding Command}">Ok</Button>
</StackPanel>
There used special proxy command, which receive element (CommandParameter) to move focus and execute. But this class requires ButtonBase for CommandParameter:
public class ButtonProxyCommand : ICommand
{
public bool CanExecute(object parameter)
{
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return false;
return btn.Command.CanExecute(btn.CommandParameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter == null)
return;
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return;
Action a = () => btn.Focus();
var op = Dispatcher.CurrentDispatcher.BeginInvoke(a);
op.Wait();
btn.Command.Execute(btn.CommandParameter);
}
private static ButtonProxyCommand _instance = null;
public static ButtonProxyCommand Instance
{
get
{
if (_instance == null)
_instance = new ButtonProxyCommand();
return _instance;
}
}
}
This is just idea, not complete solution.

What is the last event to fire when loading a new WPF/C# window?

I am trying to load a preferences window for my application and I would like the apply button to initially be disabled, then when a preference is updated, the apply button gets enabled again. I have some controls data bound to a preferences object and what happens is that after the window loads, the combobox events get triggered. Is there any event that is guaranteed to happen dead last after everything is stable?
Here is what my code looks like (the apply button is always enabled after the window loads):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_preferencesData = new PreferencesDataContext();
LayoutRoot.DataContext = _preferencesData;
ButtonApply.IsEnabled = false;
}
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ButtonApply.IsEnabled = true;
}
Is it also interesting to note that this only happens with textboxes and comboboxes, not checkboxes or radiobuttons.
Best solution for simple need
Joseph's answer is the best solution by far for your simple need: Just use data binding and let the data model handle it.
Answer to question as posed
There are more complex scenarios when you really do need control after absolutely everything has finished loading and all events have fired. There is no single event that occurs "dead last", but it is easy to effectively roll your own using the Dispatcher queue.
This is how to do it:
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
{
var x = ComputeSomething(1, 2, 3);
DoSomething(x, "Test");
}));
Everything inside the { } will be executed when WPF finishes everything at a higher priority than ContextIdle, which includes all event handlers, loaded events, input events, rendering, etc.
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be found applied in addition to any element-specific initialization you may define [note: Initialized event not fired for leaves in a logical tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional object tree construction including more constructors and getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is shown. The other steps generally don't happen until a Window is shown, but they can happen earlier if triggered manually.
The Window.ContentRendered event fulfilled my requirements.
I just did kind of the same thing behaviorly in a systray WPF app.
However, I didn't do it using event handling. I simply bound the Enabled property of my button to a property in my ViewModel, and had the property updated whenever I needed the behavior.
You can use ManagedSpy to figure this out on your own.
http://msdn.microsoft.com/en-us/magazine/cc163617.aspx
Setting the DataContext will likely fire the SelectionChanged event, and you can't rely on when exactly it's fired. Some logic checking on what exactly is selected would be more reliable:
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (myComboBox.SelectedItem == null)
{
buttonApply.IsEnabled = false;
}
else
{
buttonApply.IsEnabled = true;
}
}
The reason it's happening afterwards with your code as-is is because the event gets queued on the thread for the UI, so it's up to Windows if it will execute the next line of code in Load, or to handle the other events on the queue.
Not to throw a whole lot of stuff at you that you may or may not be familiar with, but if this is a relatively new codebase, you may want to consider using the MVVM pattern and use Commands instead of the archaic (emphasis mine) eventing model.
Order of Events in Windows Forms
Control.HandleCreated
Control.BindingContextChanged
Form.Load
Control.VisibleChanged
Form.Activated
Form.Shown

Categories

Resources