Update databindings when a TextBox changes rather than loses focus - winforms

I'm learning to develop for compact framework and I've come across an issue that's bugging me.
I've bound a couple of textboxes to some properties (firstname & lastname of a person class) and have a menuitem which just does a showmessage of the full name, and it works fairly well except that the properties only get updated once the textbox losses focus. This means that if I change the firstname and press the show name menuitem, I get the old value of firstname.
Is there a way I can force an update of the databindings, or make it so that every time a character is changed in one of the textboxes the corresponding property is updated?

If you do this, you risk putting bad data into your data object, but here is how to do this:
In your MyTextBox.DataBinding.Add() method, use the this overload with OnPropertyChanged for the DataSourceUpdateMode param instead of the default OnValidate
I again say that this is one of those things that sounds really easy, but will likely cause issues in the long run as you are 'binding' to data that has never been validated.

Just call form's ValidateChildren() in the code on the button doing the save

Related

Data binding: Different triggers for different purposes

I have a WPF MVVM data form window with data validation. A lot of the controls are text boxes. Currently, the data binding trigger is set to the default, i. e. loss of focus. This means that a field is only validated when it is likely to be filled out completely. So when deleting a number and typing another number, the transient empty value will not be displayed as input error.
But a drawback is that the Save button can only be enabled when the focus moves out of the text box. (No matter where, just out of the edited control. Assuming there is anything else focusable.) If this is the only change, the user waits for the Save button to be available and nothing happens. For the Save button, I'd like to use an immediate binding trigger. How can that be done?
Edit: Forgot to mention that my Save button (which uses ICommand) is only enabled when the input is determined modified and valid. So the data will remain unmodified until data binding updates it, and that won't happen until the focus moves to another control.
I actually had a similar question a while back and the solution I ended using was a custom DependencyProperty that kicked off a timer when a key was pressed, and only actually processed the PropertyChange notification if a specific time had passed.
This means the bound property doesn't get updated (and validated) unless the user pauses in typing for a set period of times.
The code can be found here (may need a bit of cleanup), and it is used like this:
<TextBox
local:DelayedUpdateBehavior.TargetProperty="{x:Static TextBox.TextProperty}"
local:DelayedUpdateBehavior.Milliseconds="1000"
Text="{Binding MyTextProperty, UpdateSourceTrigger=Explicit}" />
Edit: Actually this link might be better. It's a markup extension so you can use it directly from your binding. I can't remember which of these two methods I used in the past, but I know it was one of them :)
<TextBox Text="{local:DelayBinding Path=MyTextProperty, Delay='00:00:01'}" />
Assuming you're using an ICommand type interface for the button click event:
You can...Implement string properties with INotifyPropertyChanged and bind them to your textbox controls. Now in your Command canexecute method you can check to see if the property is !nullorempty.
e/ grammar
Set your Binding's UpdateSourceTrigger property to PropertyChanged. The default for TextBoxes is LostFocus.
Update: So you want to have data binding working on your TextBox and only allow numbers? Have a look at this question: Create WPF TextBox that accepts only numbers
Or use a converter and bind the Save button's IsEnabled property to your TextBox (maybe using a MultiBinding if there's more than one), and use a converter which determines if the text is a valid number and returns true or false.

MVVM detect Validation.HasError in View Model

I'm using MVVM and have most of my validation done using IDataErrorInfo and my ViewModel has an IsValid property which checks the validity of each member that needs to be validated. However I have a couple of textboxes bound to ints that can't be null, so I'm using a ValidationRule to alert the user (with a more friendly message than the "value could not be converted" one) if they blank that field out as obviously the property setter never gets called so the IDataErrorInfo code isn't called.
The problem is that I have a Save button (which is a RelayCommand) which I want disabled if there is any validation error. So the CanExecute of that command checks the VM's IsValid property. But obviously if the user blanks my int field the IDataErrorInfo knows nothing about it and currently the button won't disabled. Is there a way that the ViewModel can detect that error?
I thought I'd found a solution here
http://wpfglue.wordpress.com/2009/12/03/forwarding-the-result-of-wpf-validation-in-mvvm/
but having translated it to C# I can't get it working (the Coerce callback is never called). I don't understand dependency properties and objects very well yet (very new to WPF) and this solution looks complicated to me.
The only thing I can think to do is to get rid of the validation rule and make a nullable int wrapper, put TargetNullValue='' in the binding and then I can check them for null in the IDataErrorInfo code. I would prefer not to do this if there's a better way.
why not use string properties instead of int with IDataErrorInfo validation in your viewmodel? in your savecommand you can safely convert your string to your int values, if IDataErrorInfo has no errors of course. Using string properties with IDataErrorInfo is the most easy way.
edit: one more think, there is another problem if you not use string properties. say you have an int Property, and the user set a 10 in your textbox. so in your viewmodel you have the 10. now the user delete the 10 and set a abc in your textbox. your viewmodel still got the 10., because of the bindingconversationexception. thats why i almost use string properties. to be fair you can use behaviors for textbox to set a mask, so the user can not enter invalid data.
I can think of two strong options right away. One is to bind to a string property in your ViewModel, which in turn is programmed to only parse and store the underlying 'int' value if the string is determined to be valid. This ensures that your TextBox will always successfully store its databound value.
The second is to intercept the ValidationExceptions that occur in your View, storing them in your ViewModel via a custom Behavior. This article will essentially do exactly as you described in your question.
What you can try is BindingGroups and have a validation over the whole element, not just single properties. I used this for our modal dialogs to create a project for example, where certain settings must be set before finishing the dialog. This link explained it in good detail. This one is also quite detailed.

TextBox UpdateSourceTrigger = PropertyChanged - does it really affect performance?

The MSDN documentation states:
Bindings that are TwoWay or
OneWayToSource listen for changes in
the target property and propagate them
back to the source. This is known as
updating the source. Usually, these
updates happen whenever the target
property changes. This is fine for
check boxes and other simple controls,
but it is usually not appropriate for
text fields. Updating after every
keystroke can diminish performance and
it denies the user the usual
opportunity to backspace and fix
typing errors before committing to the
new value. Therefore, the default
UpdateSourceTrigger value of the Text
property is LostFocus and not
PropertyChanged.
I understand that in a situation where the update is going directly to a database, or across a network, or if it's an extremely large amount of data, that it could indeed diminish performance to use UpdateSourceTrigger = PropertyChanged on TextBoxes.
But if it is just updating a simple DependencyProperty, or a property of an Entity Framework object (prior to committing), would the performance hit not be negligible?
Just wondering, because I am creating a WPF app which tracks the state of the object being edited and optimizes the Save button appearance depending on whether changes have been made. I thought the easiest way to determine changes would be to catch the relevant SourceUpdated occurences as appropriate. It works optimally when UpdateSourceTrigger = PropertyChanged for the textboxes, as the user gets instant feedback that there are "saveable" changes.
The reason that you're warned about performance degradation is that for the most part, if you need to have the source property updated on every keystroke, it's because you need something to happen when the property's value changes. After all, if you didn't need that "something" to happen, you wouldn't really care when the property got updated, so long as it did eventually.
The real impact on performance depends entirely on what that "something" is. And that's totally dependent on your application. If that "something" is formatting and displaying the value in another TextBlock, doing it on every keystroke probably won't be noticeable. If it's filtering a 10,000-row DataTable and refreshing a DataGrid bound to it, it probably will.
So how do you tell? Well, there are two ways:
1) Understand your application. If you know what the application is doing when you update the source property, you can predict whether or not doing it on every keystroke is going to be a problem. When you say "I guess I was wondering whether it might seem to be fine at first, but can actually cause issues in certain situations I'm not aware of," what you're really saying is, "What happens if I don't know what my application is doing when the user presses a key?"
2) If you don't know what your application is doing when the user presses a key, profile it.
If it is suitable for your application and you don't notice a significant degradation in performance, then there is no problem setting the UpdateSourceTrigger to be PropertyChanged. In fact, if you're using an MVVM framework such as Caliburn.Micro, then it will set this as the default setting for all TextBoxes.

Is it guaranteed that all the WPF bindings will be functional when OnStartup() is called?

I am working on an application that uses WPF/C# with MVVM. I have one particular ObservableCollection<> which is bound to ListBox.
Scenario 1: When the application is running, I modify this ObservableCollection<> and ListBox is populated as expected.
Scenario 2: With new requirements I have to fill this ListBox by default with some init values. So I have added a method for initializing it in my ViewModel. I call this method in OnStartup() after initializing View & ViewModel. DataContext is also set properly. In this scenario values are updated in the ObservableCollection<>, Unfortunately they are not reflected in the ListBox.
Just to verify if anything is wrong with the OnStartup(), I added the same method call in a callback on ContentRendered from the View, instead of OnStartup() it did work fine.
So my question, When exactly MVVM guarantees that all the bindings are setup correctly?
Edit:
One more observation, if I pop a message/dialog ListBox is populated as expected. It calls ContentRendered callback. And then it populates correctly.
I am almost convinced that ContentRendered should be the function that guarantees the bindings.
I can't say with certainty that this is your issue, without seeing the change to the code, but it sounds like you may now have an initial value of null for the property to which you are binding.
If you subsequently set that property to an ObservableCollection<T>, there is no automatic change notification.
One option would be to raise a notification that the property value changed (from null to something), and then let the ObservableCollection<T> handle change notification from there.
The better solution would be to initialize the property with an empty ObservableCollection<T> from the get-go, then your initialization to default values will mean adding those values and change notification should happen as you expect.
You might get the same problem by starting with a non-null collection, then setting the property to a new instance of a collection, without raising a notification, but I'm not 100 percent certain of that.
If this is not your issue, then I'll be happy to take another look.

Put it in codebehind or in the ViewModel

I would like to get some opinions on when/if it is ok to put code in codebehind. I've only been at this for less than a year now. So, I still consider myself very "green". I come from a Delphi background. So, the learning curve has been tremendous to say the least – learning WPF, XAML, C#, Unity, Prism, MEF, .NET, MVVM, etc… Fun but very challenging.
When I first started less than a year ago, the idea in the office was no code in codebehind if at all possible and no view specific code in the VM.. So, I have racked my brain many times to determine how to push literally everything into the VM and keep what I think is view specific code out of the VM only to come up short almost every time. I’m to the point now, I’m beginning to think codebehind is not always bad or “wrong”. I have recently been trying to clean up some of our views by attempting push any codebehind into the VM which led me to find a neat factory class at http://blog.functionalfun.net/2008/09/hooking-up-commands-to-events-in-wpf.html. This allows you to bind routed events to ICommands in the VM. It works like a charm and I was able to significantly reduce some of our codebehind with it. However, after doing so, I’m now questioning my decision to do so. My approach followed the philosophy that codebehind was bad/wrong unless absolutely required. Now that I have had a little time to think about it, I’m not so sure the refactor was the best idea.
The following is an example of a view I refactored. We have a new account view where the user enters an SSN and must rekey the SSN before the new account can be created. The view has a label that displays text to tell the user if the SSN and rekey SSN do not match and the OK button is not enabled until they both match. Once the SSN and rekey SSN match, the label disappears (yes I know… I hate that but I’m just the developer) and the OK button is enabled. So, the hiding/showing of the label and enable/disable of the OK button is triggered from the TextChanged events in the SSN and rekey SSN text boxes. Initially I had logic in codebehind to compare the two textbox values and set viewmodel properties appropriately to update the visible property of the label and the OK button’s enabled property (yes their properties are bound in XAML). After finding this new factory class, I used it to push all of the code into the viewmodel and the view works just like it did before just without codebehind. After successfully refactoring the view, I am now second guessing the decision to refactor.
My concern is, what if we want to have a different view in the future and the new view doesn’t want to handle mismatched SSN’s in that fashion. Maybe the new view would allow the user to type in mismatching SSNs and then display an error message when the OK button is clicked. Should the new view have to accept the extra overhead of code in the viewmodel executing for every keypress in the text boxes? That just doesn’t sound right to me. I’m starting to think the viewmodel should contain what is required to support what the view needs but not do everything for the view. The view should be able to make decisions based on something in the viewmodel but not depend on the VM to hold its hand – right?
The XAML and matching .cs file make up the view. Because of that there's nothing wrong with putting code in the codebehind as long as it's for the view itself.
There is certainly nothing "wrong" with codebehind.
Here's how I'd handle your SSN example:
Bind both SSN fields to properties on the view model
Bind the OK button to a command in the view model (I'm a big fan of the Delegate Command concept)
The command should implement the CanExecute portion and only return true when both SSNs match.
It doesn't seem that the SSN matching is a view concern, it's a business practice. It belongs in the view model. What would be in the view is how your application shows that the SSNs do or don't match.

Resources