Data binding: Different triggers for different purposes - wpf

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.

Related

UI IsDirty versus Entity IsDirty

I am well aware of the INotifyPropertyChanged and INotifyCollectionChanged for WPF binding. This has been implemented and is working as expected. Therefore, changes to a property on our entities will set their "IsDirty" flag. Which in turn, turns on the Save functionality, all via binding, beautiful you may say.
However, we recently had a change request raised to request that the Save button should become enabled as soon as the field changes (i.e. use presses the first keystroke), much like the undo application command does when you type in a textbox. Therefore the save button must know when every field is changed on the form instead of the property being changed in the entity via binding.
The main reason for this request for help, is one of our forms only has one field that changes (everything else normally remains default), but the user still has to tab out of the field to enable the Save button, instead of just being able to save straight away.
You need to change the UpdateSourceTrigger to PropertyChanged
This will update the binding everytime the text changes rather than LostFocus - which is the default for a textbox.
<TextBlock Name="txtName" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

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.

WPF DataGrid with textbox in template column not firing property changed

I have a datagrid with a template column, and in that template is a textbox. It's bound to the 'Quantity' property of the object in the collection that makes up itemssource. Quantity is an integer. When I add an item to the datagrid, I am adding an event handler to the PropertyChanged event of the item:
EnteredPart.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(p_PropertyChanged);
This works fine when the user enters an integer in the textbox... the p_PropertyChanged handler fires and I can disable buttons and alter quantities as necessary. When the user enters a non-integer, the handler doesn't get called, I just get a red outline around the textbox. Because of this, I can't disable the necessary buttons when I need to (they should be disabled with the quantity is not legit.) Is there any way that I can do something about this?
EDIT: I tried changing the Quantity property to a string, and this caused the property changed handler to be called when non-integral values are entered. However, I then added validation to the textbox to check for this, and if the Validate method returns false, the property changed handler once again ceases to be hit. Is there any way at all to get both validation and property changed notifications??
EDIT 2: Here another instance of this problem I'm having, in another location. I have a form for adding/editing phone numbers. The phone number textbox looks like this:
<TextBox >
<TextBox.Text>
<Binding Path="Phone.Number">
<Binding.ValidationRules>
<local:PhoneValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Upon clicking a save button, I need to check in my viewmodel if the number is valid, because if it's not, I don't want to run the save command. However, it doesn't seem as if there's any way to do that, because if the validation fails, then the Phone.Number property has a null value, and I have no way to check to see if I should run the save command. I either need access to the error state (which I thought would work by Validation.GetErrors, but doesn't), or to the actual text within the textbox, which isn't available in the viewmodel.
Just for the record, validation in the View part has many drawbacks if you are in an MVVM architecture.
You'd try here to check your model's content, in the view: you'd therefore break MVVM's architecture by calling your model in the view.
Using IDataErrorInfo will help you to fulfill MVVM's main objective (ie. clearly separate the three parts).
Just an example:
I think here you are just performing a very small validation (just check if it is an int or not).
But in a different environment, assume that your model is much more complicated and needs a deeper validation. Using IDataErrorInfo will help you to check deeply in your model without calling it from the view.
As a matter of fact, in my personal experience, as I regularly work with large and highly correlated datasets, I cannot even picture using validation without IDataErrorInfo because it'd cost me too much to investigate in all the data presented and find potential errors

Which event should be used to update a Model from TextBox (LostFocus, LostKeyboardFocus, etc) in WPF? How to set precedence of events in WPF?

I have an application in which there are lot of TextBoxes and some Buttons like Save, SaveAs,etc.
When the user edits a TextBox, I have to check the DataBase for some range, validate the range and update the DataBase.
If there is any error in value entered by user,then I should not allow the TextBox to lose focus.
I was using LostFocus event for this and it was working fine until lately I discovered a bug in my application.
Bug : The user edits a value in TextBox and then clicks on Save button; the LostFocus event is not called and so Database is not getting updated :(
Now my question is which event should I use in TextBox to update the DataBase. I tried TextChanged event but it validates for every character and making my application slow. I am confused in chosing the right event for this kind of application!
Note :** The Buttons are in different UserControl !
EDIT 1 : I have some Commands attached to click of Buttons, these Commands are getting executed before LostFocus !! Can I set precedence or something like attached behaviours or commands should get executed after LostFocus !!
EDIT 2 : I was just debugging the application by disabling some commands, what I found was in some cases, the DelegateCommand gets executed before LostFocus, so I want to avoid that. How can I go about it ? I felt during development its impossible to developa pure MVVM application so I am kind of using a bit of codebehind !
Trapping the keyboard focus within a control is usually a sign of bad UI design - it's pretty user-hostile to force the user to fix data in a control before he can type anywhere else in the UI.
That said, you shouldn't be using events at all here. You're trying to write a Windows Forms application in WPF. You should write a WPF application.
Create a class that is a logical model of your view - i.e., there's a string property for the text box and a Command property (or, more likely, a RelayCommand) for the Save button. Bind the text box to the string property, e.g.:
<TextBox Text="{Binding MyTextProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Because the UpdateSourceTrigger is PropertyChanged, the source object will get updated every time the user types a character.
Bind the button to the command property, e.g.:
<Button CommandBinding="{Binding SaveCommand}">Save</Button>
Implement the appropriate CanSave and Save methods that the RelayCommand (as described in Josh Smith's essential article on the MVVM pattern) requires, so that the button is enabled when the string property is valid and disabled when it's not.
I think the best approach is preventing a user to proceed until all valid information has been gathered.
Just like an installation wizard with Terms & Conditions Dialog and Next button. Until you check the I Agree checkbox, Next button is disabled.
This way, you don't have to worry about user proceeding without providing valid information. This way, you can use any event on TextBox to validate your data.

Silverlight Combobox data binding : Basic and quick way?

Please tell me the basic and quick way to bind a Collection(List,etc) to a combobox and handle the selection changed event and get the selected item.
This is quite easy. You can do this with XAML+Code or just code. I won't type out a complete solution as I feel you'll benefit more from completing that part yourself. I've assumed here that you already have some XAML declaring a combo box, so I've just shown some code (in C# as you didn't state what language you were using), just know that the event handler could easily be assigned via XAML instead.
this.combo.SelectionChanged +=
new SelectionChangedEventHandler(comboProjects_SelectionChanged);
this.combo.ItemsSource = myCollectionOfItems;
This sets up an event handler for the selection changing and also binds the combo to a collection, which it uses to source its items (hence the name, ItemsSource).
Then, in the SelectionChanged event handler, you can get the SelectedItem property to find out what is selected.
Note that there are some caveats with binding in the ComboBox, so you may find these links useful:
http://silverlight.net/forums/t/63616.aspx
http://silverlight.net/forums/p/87111/202335.aspx#202335

Resources